diff options
Diffstat (limited to 'OpenKeychain/src/main/java/org')
49 files changed, 919 insertions, 753 deletions
| diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java index 67fa30a44..30d855a74 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java @@ -19,6 +19,8 @@ package org.sufficientlysecure.keychain;  import android.os.Environment; +import org.spongycastle.bcpg.HashAlgorithmTags; +import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags;  import org.spongycastle.jce.provider.BouncyCastleProvider;  import java.io.File; @@ -26,6 +28,8 @@ import java.io.File;  public final class Constants {      public static final boolean DEBUG = BuildConfig.DEBUG; +    public static final boolean DEBUG_LOG_DB_QUERIES = false; +    public static final boolean DEBUG_SYNC_REMOVE_CONTACTS = false;      public static final String TAG = "Keychain"; @@ -33,6 +37,7 @@ public final class Constants {      public static final String ACCOUNT_NAME = "OpenKeychain";      public static final String ACCOUNT_TYPE = PACKAGE_NAME + ".account"; +    public static final String CUSTOM_CONTACT_DATA_MIME_TYPE = "vnd.android.cursor.item/vnd.org.sufficientlysecure.keychain.key";      // as defined in http://tools.ietf.org/html/rfc3156, section 7      public static final String NFC_MIME = "application/pgp-keys"; @@ -49,8 +54,6 @@ public final class Constants {      public static final String INTENT_PREFIX = PACKAGE_NAME + ".action.";      public static final String EXTRA_PREFIX = PACKAGE_NAME + "."; -    public static final String CUSTOM_CONTACT_DATA_MIME_TYPE = "vnd.android.cursor.item/vnd.org.sufficientlysecure.keychain.key"; -      public static final int TEMPFILE_TTL = 24 * 60 * 60 * 1000; // 1 day      public static final String SAFESLINGER_SERVER = "safeslinger-openpgp.appspot.com"; @@ -61,24 +64,17 @@ public final class Constants {      }      public static final class Pref { -        public static final String DEFAULT_ENCRYPTION_ALGORITHM = "defaultEncryptionAlgorithm"; -        public static final String DEFAULT_HASH_ALGORITHM = "defaultHashAlgorithm"; -        public static final String DEFAULT_ASCII_ARMOR = "defaultAsciiArmor"; -        public static final String DEFAULT_MESSAGE_COMPRESSION = "defaultMessageCompression"; -        public static final String DEFAULT_FILE_COMPRESSION = "defaultFileCompression";          public static final String PASSPHRASE_CACHE_TTL = "passphraseCacheTtl";          public static final String PASSPHRASE_CACHE_SUBS = "passphraseCacheSubs";          public static final String LANGUAGE = "language";          public static final String KEY_SERVERS = "keyServers";          public static final String PREF_DEFAULT_VERSION = "keyServersDefaultVersion"; -        public static final String WRITE_VERSION_HEADER = "writeVersionHeader";          public static final String FIRST_TIME = "firstTime"; -        public static final String SHOW_ADVANCED_TABS = "showAdvancedTabs";          public static final String CACHED_CONSOLIDATE = "cachedConsolidate";          public static final String SEARCH_KEYSERVER = "search_keyserver_pref";          public static final String SEARCH_KEYBASE = "search_keybase_pref";          public static final String USE_DEFAULT_YUBIKEY_PIN = "useDefaultYubikeyPin"; -        public static final String USE_NUMKEYPAD_FOR_YUBIKEY_PIN="useNumKeypadForYubikeyPin"; +        public static final String USE_NUMKEYPAD_FOR_YUBIKEY_PIN = "useNumKeypadForYubikeyPin";      }      public static final class Defaults { @@ -90,4 +86,5 @@ public final class Constants {          public static final int none = 0;          public static final int symmetric = -1;      } +  } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportExportOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportExportOperation.java index 8f10377cd..20dba95e9 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportExportOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportExportOperation.java @@ -505,10 +505,6 @@ public class ImportExportOperation extends BaseOperation {                  // Create an output stream                  try {                      arOutStream = new ArmoredOutputStream(outStream); -                    String version = PgpHelper.getVersionForHeader(mContext); -                    if (version != null) { -                        arOutStream.setHeader("Version", version); -                    }                      log.add(LogType.MSG_EXPORT_PUBLIC, 1, KeyFormattingUtils.beautifyKeyId(keyId)); @@ -533,10 +529,6 @@ public class ImportExportOperation extends BaseOperation {                  if (exportSecret && cursor.getInt(3) > 0) {                      try {                          arOutStream = new ArmoredOutputStream(outStream); -                        String version = PgpHelper.getVersionForHeader(mContext); -                        if (version != null) { -                            arOutStream.setHeader("Version", version); -                        }                          // export secret key part                          log.add(LogType.MSG_EXPORT_SECRET, 2, KeyFormattingUtils.beautifyKeyId(keyId)); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java index f79900aab..a96cec8cf 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java @@ -38,6 +38,8 @@ import java.util.ArrayList;  import java.util.Arrays;  import java.util.Iterator;  import java.util.List; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap;  /** Represent the result of an operation.   * @@ -51,6 +53,56 @@ import java.util.List;  public abstract class OperationResult implements Parcelable {      public static final String EXTRA_RESULT = "operation_result"; +    public static final UUID NULL_UUID = new UUID(0,0); + +    /** +     * A HashMap of UUID:OperationLog which contains logs that we don't need +     * to care about. This is used such that when we become parceled, we are +     * well below the 1Mbit boundary that is specified. +     */ +    private static ConcurrentHashMap<UUID, OperationLog> dehydratedLogs; +    static { +        // Static initializer for ConcurrentHashMap +        dehydratedLogs = new ConcurrentHashMap<UUID,OperationLog>(); +    } + +    /** +     * Dehydrate a log (such that it is available after deparcelization) +     * +     * Returns the NULL uuid (0) if you hand it null. +     * @param log An OperationLog to dehydrate +     * @return a UUID, the ticket for your dehydrated log +     * +     */ +    private static UUID dehydrateLog(OperationLog log) { +        if(log == null) { +            return NULL_UUID; +        } +        else { +            UUID ticket = UUID.randomUUID(); +            dehydratedLogs.put(ticket, log); +            return ticket; +        } +    } + +    /*** +     * Rehydrate a log after going through parcelization, invalidating its place in the +     * dehydration pool. +     * This is used such that when parcelized, the parcel is no larger than 1mbit. +     * @param ticket A UUID ticket that identifies the log in question. +     * @return An OperationLog. +     */ +    private static OperationLog rehydrateLog(UUID ticket) { +        // UUID.equals isn't well documented; we use compareTo instead. +        if( NULL_UUID.compareTo(ticket) == 0 ) { +            return null; +        } +        else { +            OperationLog log = dehydratedLogs.get(ticket); +            dehydratedLogs.remove(ticket); +            return log; +        } +    }      /** Holds the overall result, the number specifying varying degrees of success:       *  - The first bit is 0 on overall success, 1 on overall failure @@ -65,7 +117,7 @@ public abstract class OperationResult implements Parcelable {      public static final int RESULT_WARNINGS = 4;      /// A list of log entries tied to the operation result. -    final OperationLog mLog; +    protected OperationLog mLog;      public OperationResult(int result, OperationLog log) {          mResult = result; @@ -74,8 +126,11 @@ public abstract class OperationResult implements Parcelable {      public OperationResult(Parcel source) {          mResult = source.readInt(); -        mLog = new OperationLog(); -        mLog.addAll(source.createTypedArrayList(LogEntryParcel.CREATOR)); +        long mostSig = source.readLong(); +        long leastSig = source.readLong(); +        UUID mTicket = new UUID(mostSig, leastSig); +        // fetch the dehydrated log out of storage (this removes it from the dehydration pool) +        mLog = rehydrateLog(mTicket);      }      public int getResult() { @@ -571,6 +626,7 @@ public abstract class OperationResult implements Parcelable {          MSG_DC_ERROR_NO_DATA (LogLevel.ERROR, R.string.msg_dc_error_no_data),          MSG_DC_ERROR_NO_KEY (LogLevel.ERROR, R.string.msg_dc_error_no_key),          MSG_DC_ERROR_PGP_EXCEPTION (LogLevel.ERROR, R.string.msg_dc_error_pgp_exception), +        MSG_DC_ERROR_UNSUPPORTED_HASH_ALGO (LogLevel.ERROR, R.string.msg_dc_error_unsupported_hash_algo),          MSG_DC_INTEGRITY_CHECK_OK (LogLevel.INFO, R.string.msg_dc_integrity_check_ok),          MSG_DC_OK_META_ONLY (LogLevel.OK, R.string.msg_dc_ok_meta_only),          MSG_DC_OK (LogLevel.OK, R.string.msg_dc_ok), @@ -585,6 +641,7 @@ public abstract class OperationResult implements Parcelable {          MSG_DC_TRAIL_SYM (LogLevel.DEBUG, R.string.msg_dc_trail_sym),          MSG_DC_TRAIL_UNKNOWN (LogLevel.DEBUG, R.string.msg_dc_trail_unknown),          MSG_DC_UNLOCKING (LogLevel.INFO, R.string.msg_dc_unlocking), +        MSG_DC_OLD_SYMMETRIC_ENCRYPTION_ALGO (LogLevel.WARN, R.string.msg_dc_old_symmetric_encryption_algo),          // verify signed literal data          MSG_VL (LogLevel.INFO, R.string.msg_vl), @@ -707,6 +764,13 @@ public abstract class OperationResult implements Parcelable {          MSG_DEL_CONSOLIDATE (LogLevel.DEBUG, R.string.msg_del_consolidate),          MSG_DEL_OK (LogLevel.OK, R.plurals.msg_del_ok),          MSG_DEL_FAIL (LogLevel.WARN, R.plurals.msg_del_fail), + +        //export log +        MSG_EXPORT_LOG(LogLevel.START,R.string.msg_export_log_start), +        MSG_EXPORT_LOG_EXPORT_ERROR_NO_FILE(LogLevel.ERROR,R.string.msg_export_log_error_no_file), +        MSG_EXPORT_LOG_EXPORT_ERROR_FOPEN(LogLevel.ERROR,R.string.msg_export_log_error_fopen), +        MSG_EXPORT_LOG_EXPORT_ERROR_WRITING(LogLevel.ERROR,R.string.msg_export_log_error_writing), +        MSG_EXPORT_LOG_EXPORT_SUCCESS (LogLevel.OK, R.string.msg_export_log_success),          ;          public final int mMsgId; @@ -739,9 +803,11 @@ public abstract class OperationResult implements Parcelable {      @Override      public void writeToParcel(Parcel dest, int flags) {          dest.writeInt(mResult); -        if (mLog != null) { -            dest.writeTypedList(mLog.toList()); -        } +        // Get a ticket for our log. +        UUID mTicket = dehydrateLog(mLog); +        // And write out the UUID most and least significant bits. +        dest.writeLong(mTicket.getMostSignificantBits()); +        dest.writeLong(mTicket.getLeastSignificantBits());      }      public static class OperationLog implements Iterable<LogEntryParcel> { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java index 40f2f48ad..fe5db8c6d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java @@ -20,6 +20,7 @@ package org.sufficientlysecure.keychain.pgp;  import org.spongycastle.bcpg.HashAlgorithmTags;  import org.spongycastle.bcpg.S2K; +import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags;  import org.spongycastle.openpgp.PGPException;  import org.spongycastle.openpgp.PGPPrivateKey;  import org.spongycastle.openpgp.PGPPublicKey; @@ -44,6 +45,7 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;  import org.sufficientlysecure.keychain.util.IterableIterator;  import org.sufficientlysecure.keychain.util.Log; +import java.util.ArrayList;  import java.util.Date;  import java.util.HashMap;  import java.util.LinkedList; @@ -137,7 +139,7 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {              // It means the passphrase is empty              return SecretKeyType.PASSPHRASE_EMPTY;          } catch (PGPException e) { -            HashMap<String,String> notation = getRing().getLocalNotationData(); +            HashMap<String, String> notation = getRing().getLocalNotationData();              if (notation.containsKey("unlock.pin@sufficientlysecure.org")                      && "1".equals(notation.get("unlock.pin@sufficientlysecure.org"))) {                  return SecretKeyType.PIN; @@ -176,33 +178,13 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {      }      /** -     * Returns a list of all supported hash algorithms. This list is currently hardcoded to return -     * a limited set of algorithms supported by Yubikeys. -     * -     * @return +     * Returns a list of all supported hash algorithms.       */ -    public LinkedList<Integer> getSupportedHashAlgorithms() { -        LinkedList<Integer> supported = new LinkedList<>(); - -        if (mPrivateKeyState == PRIVATE_KEY_STATE_DIVERT_TO_CARD) { -            // No support for MD5 -            supported.add(HashAlgorithmTags.RIPEMD160); -            supported.add(HashAlgorithmTags.SHA1); -            supported.add(HashAlgorithmTags.SHA224); -            supported.add(HashAlgorithmTags.SHA256); -            supported.add(HashAlgorithmTags.SHA384); -            supported.add(HashAlgorithmTags.SHA512); // preferred is latest -        } else { -            supported.add(HashAlgorithmTags.MD5); -            supported.add(HashAlgorithmTags.RIPEMD160); -            supported.add(HashAlgorithmTags.SHA1); -            supported.add(HashAlgorithmTags.SHA224); -            supported.add(HashAlgorithmTags.SHA256); -            supported.add(HashAlgorithmTags.SHA384); -            supported.add(HashAlgorithmTags.SHA512); // preferred is latest -        } +    public ArrayList<Integer> getSupportedHashAlgorithms() { +        // TODO: intersection between preferred hash algos of this key and PgpConstants.PREFERRED_HASH_ALGORITHMS +        // choose best algo -        return supported; +        return PgpConstants.sPreferredHashAlgorithms;      }      private PGPContentSignerBuilder getContentSignerBuilder(int hashAlgo, byte[] nfcSignedHash, @@ -358,7 +340,7 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {      }      // HACK, for TESTING ONLY!! -    PGPPrivateKey getPrivateKey () { +    PGPPrivateKey getPrivateKey() {          return mPrivateKey;      } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKeyRing.java index b5f6a5b09..97b5fa6fe 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKeyRing.java @@ -19,11 +19,11 @@  package org.sufficientlysecure.keychain.pgp;  import org.spongycastle.openpgp.PGPKeyRing; -import org.spongycastle.openpgp.PGPObjectFactory;  import org.spongycastle.openpgp.PGPPublicKey;  import org.spongycastle.openpgp.PGPSecretKey;  import org.spongycastle.openpgp.PGPSecretKeyRing;  import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.jcajce.JcaPGPObjectFactory;  import org.sufficientlysecure.keychain.Constants;  import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;  import org.sufficientlysecure.keychain.util.IterableIterator; @@ -45,7 +45,7 @@ public class CanonicalizedSecretKeyRing extends CanonicalizedKeyRing {      public CanonicalizedSecretKeyRing(byte[] blob, boolean isRevoked, int verified)      {          super(verified); -        PGPObjectFactory factory = new PGPObjectFactory(blob); +        JcaPGPObjectFactory factory = new JcaPGPObjectFactory(blob);          PGPKeyRing keyRing = null;          try {              if ((keyRing = (PGPKeyRing) factory.nextObject()) == null) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConstants.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConstants.java new file mode 100644 index 000000000..90991ba15 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConstants.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +package org.sufficientlysecure.keychain.pgp; + +import org.spongycastle.bcpg.CompressionAlgorithmTags; +import org.spongycastle.bcpg.HashAlgorithmTags; +import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags; + +import java.util.ArrayList; + +public class PgpConstants { + +    public static ArrayList<Integer> sPreferredSymmetricAlgorithms = new ArrayList<>(); +    public static ArrayList<Integer> sPreferredHashAlgorithms = new ArrayList<>(); +    public static ArrayList<Integer> sPreferredCompressionAlgorithms = new ArrayList<>(); + +    // TODO: use hashmaps for contains in O(1) and intersections! + +    /* +     * Most preferred is first +     * These arrays are written as preferred algorithms into the keys on creation. +     * Other implementations may choose to honor this selection. +     * +     * These lists also define the only algorithms which are used in OpenKeychain. +     * We do not support algorithms such as MD5 +     */ +    static { +        sPreferredSymmetricAlgorithms.add(SymmetricKeyAlgorithmTags.AES_256); +        sPreferredSymmetricAlgorithms.add(SymmetricKeyAlgorithmTags.AES_192); +        sPreferredSymmetricAlgorithms.add(SymmetricKeyAlgorithmTags.AES_128); +        sPreferredSymmetricAlgorithms.add(SymmetricKeyAlgorithmTags.TWOFISH); + +        // NOTE: some implementations do not support SHA512, thus we choose SHA256 as default (Mailvelope?) +        sPreferredHashAlgorithms.add(HashAlgorithmTags.SHA256); +        sPreferredHashAlgorithms.add(HashAlgorithmTags.SHA512); +        sPreferredHashAlgorithms.add(HashAlgorithmTags.SHA384); +        sPreferredHashAlgorithms.add(HashAlgorithmTags.SHA224); +        sPreferredHashAlgorithms.add(HashAlgorithmTags.SHA1); +        sPreferredHashAlgorithms.add(HashAlgorithmTags.RIPEMD160); + +        sPreferredCompressionAlgorithms.add(CompressionAlgorithmTags.ZLIB); +        sPreferredCompressionAlgorithms.add(CompressionAlgorithmTags.BZIP2); +        sPreferredCompressionAlgorithms.add(CompressionAlgorithmTags.ZIP); +    } + +    /* +     * Note: s2kcount is a number between 0 and 0xff that controls the +     * number of times to iterate the password hash before use. More +     * iterations are useful against offline attacks, as it takes more +     * time to check each password. The actual number of iterations is +     * rather complex, and also depends on the hash function in use. +     * Refer to Section 3.7.1.3 in rfc4880.txt. Bigger numbers give +     * you more iterations.  As a rough rule of thumb, when using +     * SHA256 as the hashing function, 0x10 gives you about 64 +     * iterations, 0x20 about 128, 0x30 about 256 and so on till 0xf0, +     * or about 1 million iterations. The maximum you can go to is +     * 0xff, or about 2 million iterations. +     * from http://kbsriram.com/2013/01/generating-rsa-keys-with-bouncycastle.html +     * +     * Bouncy Castle default: 0x60 +     * kbsriram proposes: 0xc0 +     * OpenKeychain: 0x90 +     */ +    public static final int SECRET_KEY_ENCRYPTOR_S2K_COUNT = 0x90; +    public static final int SECRET_KEY_ENCRYPTOR_HASH_ALGO = HashAlgorithmTags.SHA256; +    public static final int SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO = SymmetricKeyAlgorithmTags.AES_256; +    public static final int SECRET_KEY_SIGNATURE_HASH_ALGO = HashAlgorithmTags.SHA256; +    // NOTE: only SHA1 is supported for key checksum calculations in OpenPGP, +    // see http://tools.ietf.org/html/rfc488 0#section-5.5.3 +    public static final int SECRET_KEY_SIGNATURE_CHECKSUM_HASH_ALGO = HashAlgorithmTags.SHA1; + +    public static interface OpenKeychainSymmetricKeyAlgorithmTags extends SymmetricKeyAlgorithmTags { +        public static final int USE_PREFERRED = -1; +    } + +    public static interface OpenKeychainHashAlgorithmTags extends HashAlgorithmTags { +        public static final int USE_PREFERRED = -1; +    } + +    public static interface OpenKeychainCompressionAlgorithmTags extends CompressionAlgorithmTags { +        public static final int USE_PREFERRED = -1; +    } + +    public static int[] getAsArray(ArrayList<Integer> list) { +        int[] array = new int[list.size()]; +        for (int i = 0; i < list.size(); i++) { +            array[i] = list.get(i); +        } +        return array; +    } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java index 2ba0b6231..14bc56538 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java @@ -563,6 +563,7 @@ public class PgpDecryptVerify extends BaseOperation {          log.add(LogType.MSG_DC_PREP_STREAMS, indent);          // we made sure above one of these two would be true +        int symmetricEncryptionAlgo;          if (symmetricPacketFound) {              currentProgress += 2;              updateProgress(R.string.progress_preparing_streams, currentProgress, 100); @@ -576,6 +577,7 @@ public class PgpDecryptVerify extends BaseOperation {              clear = encryptedDataSymmetric.getDataStream(decryptorFactory);              encryptedData = encryptedDataSymmetric; +            symmetricEncryptionAlgo = encryptedDataSymmetric.getSymmetricAlgorithm(decryptorFactory);          } else if (asymmetricPacketFound) {              currentProgress += 2;              updateProgress(R.string.progress_extracting_key, currentProgress, 100); @@ -598,6 +600,8 @@ public class PgpDecryptVerify extends BaseOperation {                  PublicKeyDataDecryptorFactory decryptorFactory                          = secretEncryptionKey.getDecryptorFactory(mDecryptedSessionKey);                  clear = encryptedDataAsymmetric.getDataStream(decryptorFactory); + +                symmetricEncryptionAlgo = encryptedDataAsymmetric.getSymmetricAlgorithm(decryptorFactory);              } catch (NfcSyncPublicKeyDataDecryptorFactoryBuilder.NfcInteractionNeeded e) {                  log.add(LogType.MSG_DC_PENDING_NFC, indent + 1);                  DecryptVerifyResult result = @@ -614,6 +618,11 @@ public class PgpDecryptVerify extends BaseOperation {              return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);          } +        // Warn about old encryption algorithms! +        if (!PgpConstants.sPreferredSymmetricAlgorithms.contains(symmetricEncryptionAlgo)) { +            log.add(LogType.MSG_DC_OLD_SYMMETRIC_ENCRYPTION_ALGO, indent + 1); +        } +          JcaPGPObjectFactory plainFact = new JcaPGPObjectFactory(clear);          Object dataChunk = plainFact.nextObject();          OpenPgpSignatureResultBuilder signatureResultBuilder = new OpenPgpSignatureResultBuilder(); @@ -811,6 +820,13 @@ public class PgpDecryptVerify extends BaseOperation {                  } else {                      log.add(LogType.MSG_DC_CLEAR_SIGNATURE_BAD, indent + 1);                  } + +                // Don't allow verification of old hash algorithms! +                if (!PgpConstants.sPreferredHashAlgorithms.contains(signature.getHashAlgorithm())) { +                    validSignature = false; +                    log.add(LogType.MSG_DC_ERROR_UNSUPPORTED_HASH_ALGO, indent + 1); +                } +                  signatureResultBuilder.setValidSignature(validSignature);              } @@ -936,6 +952,13 @@ public class PgpDecryptVerify extends BaseOperation {                  } else {                      log.add(LogType.MSG_DC_CLEAR_SIGNATURE_BAD, indent + 1);                  } + +                // Don't allow verification of old hash algorithms! +                if (!PgpConstants.sPreferredHashAlgorithms.contains(signature.getHashAlgorithm())) { +                    validSignature = false; +                    log.add(LogType.MSG_DC_ERROR_UNSUPPORTED_HASH_ALGO, indent + 1); +                } +                  signatureResultBuilder.setValidSignature(validSignature);              } catch (SignatureException e) { @@ -1024,6 +1047,13 @@ public class PgpDecryptVerify extends BaseOperation {              } else {                  log.add(LogType.MSG_DC_CLEAR_SIGNATURE_BAD, indent + 1);              } + +            // Don't allow verification of old hash algorithms! +            if (!PgpConstants.sPreferredHashAlgorithms.contains(signature.getHashAlgorithm())) { +                validSignature = false; +                log.add(LogType.MSG_DC_ERROR_UNSUPPORTED_HASH_ALGO, indent + 1); +            } +              signatureResultBuilder.setValidSignature(validSignature);          } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java index 12de2f637..d8b86a18c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java @@ -47,26 +47,6 @@ public class PgpHelper {              ".*?(-----BEGIN PGP PUBLIC KEY BLOCK-----.*?-----END PGP PUBLIC KEY BLOCK-----).*",              Pattern.DOTALL); -    public static String getVersion(Context context) { -        String version; -        try { -            PackageInfo pi = context.getPackageManager().getPackageInfo(Constants.PACKAGE_NAME, 0); -            version = pi.versionName; -            return version; -        } catch (NameNotFoundException e) { -            Log.e(Constants.TAG, "Version could not be retrieved!", e); -            return "0.0"; -        } -    } - -    public static String getVersionForHeader(Context context) { -        if(Preferences.getPreferences(context).getWriteVersionHeader()){ -            return "OpenKeychain v" + getVersion(context); -        } else { -            return null; -        } -    } -      /**       * Deletes file securely by overwriting it with random data before deleting it.       * <p/> diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java index 1a251eb79..8fb5392e3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java @@ -18,9 +18,7 @@  package org.sufficientlysecure.keychain.pgp; -import org.spongycastle.bcpg.CompressionAlgorithmTags;  import org.spongycastle.bcpg.HashAlgorithmTags; -import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags;  import org.spongycastle.bcpg.sig.Features;  import org.spongycastle.bcpg.sig.KeyFlags;  import org.spongycastle.jce.spec.ElGamalParameterSpec; @@ -90,48 +88,6 @@ public class PgpKeyOperation {      private Stack<Progressable> mProgress;      private AtomicBoolean mCancelled; -    // most preferred is first -    private static final int[] PREFERRED_SYMMETRIC_ALGORITHMS = new int[]{ -            SymmetricKeyAlgorithmTags.AES_256, -            SymmetricKeyAlgorithmTags.AES_192, -            SymmetricKeyAlgorithmTags.AES_128, -            SymmetricKeyAlgorithmTags.CAST5 -    }; -    private static final int[] PREFERRED_HASH_ALGORITHMS = new int[]{ -            HashAlgorithmTags.SHA512, -            HashAlgorithmTags.SHA384, -            HashAlgorithmTags.SHA224, -            HashAlgorithmTags.SHA256, -            HashAlgorithmTags.RIPEMD160 -    }; -    private static final int[] PREFERRED_COMPRESSION_ALGORITHMS = new int[]{ -            CompressionAlgorithmTags.ZLIB, -            CompressionAlgorithmTags.BZIP2, -            CompressionAlgorithmTags.ZIP -    }; - -    /* -     * Note: s2kcount is a number between 0 and 0xff that controls the -     * number of times to iterate the password hash before use. More -     * iterations are useful against offline attacks, as it takes more -     * time to check each password. The actual number of iterations is -     * rather complex, and also depends on the hash function in use. -     * Refer to Section 3.7.1.3 in rfc4880.txt. Bigger numbers give -     * you more iterations.  As a rough rule of thumb, when using -     * SHA256 as the hashing function, 0x10 gives you about 64 -     * iterations, 0x20 about 128, 0x30 about 256 and so on till 0xf0, -     * or about 1 million iterations. The maximum you can go to is -     * 0xff, or about 2 million iterations. -     * from http://kbsriram.com/2013/01/generating-rsa-keys-with-bouncycastle.html -     * -     * Bouncy Castle default: 0x60 -     * kbsriram proposes 0xc0 -     * we use 0x90, a good trade-off between usability and security against offline attacks -     */ -    private static final int SECRET_KEY_ENCRYPTOR_S2K_COUNT = 0x90; -    private static final int SECRET_KEY_ENCRYPTOR_HASH_ALGO = HashAlgorithmTags.SHA256; -    private static final int SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO = SymmetricKeyAlgorithmTags.AES_256; -      public PgpKeyOperation(Progressable progress) {          super();          if (progress != null) { @@ -345,14 +301,14 @@ public class PgpKeyOperation {              // Build key encrypter and decrypter based on passphrase              PGPDigestCalculator encryptorHashCalc = new JcaPGPDigestCalculatorProviderBuilder() -                    .build().get(SECRET_KEY_ENCRYPTOR_HASH_ALGO); +                    .build().get(PgpConstants.SECRET_KEY_ENCRYPTOR_HASH_ALGO);              PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder( -                    SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc, SECRET_KEY_ENCRYPTOR_S2K_COUNT) +                    PgpConstants.SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, +                    encryptorHashCalc, PgpConstants.SECRET_KEY_ENCRYPTOR_S2K_COUNT)                      .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build("".toCharArray()); -            // NOTE: only SHA1 is supported for key checksum calculations.              PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder() -                    .build().get(HashAlgorithmTags.SHA1); +                    .build().get(PgpConstants.SECRET_KEY_SIGNATURE_CHECKSUM_HASH_ALGO);              PGPSecretKey masterSecretKey = new PGPSecretKey(keyPair.getPrivateKey(), keyPair.getPublicKey(),                      sha1Calc, true, keyEncryptor); @@ -879,14 +835,14 @@ public class PgpKeyOperation {                  PGPSecretKey sKey; {                      // Build key encrypter and decrypter based on passphrase                      PGPDigestCalculator encryptorHashCalc = new JcaPGPDigestCalculatorProviderBuilder() -                            .build().get(SECRET_KEY_ENCRYPTOR_HASH_ALGO); +                            .build().get(PgpConstants.SECRET_KEY_ENCRYPTOR_HASH_ALGO);                      PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder( -                            SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc, SECRET_KEY_ENCRYPTOR_S2K_COUNT) +                            PgpConstants.SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc, +                            PgpConstants.SECRET_KEY_ENCRYPTOR_S2K_COUNT)                              .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); -                    // NOTE: only SHA1 is supported for key checksum calculations.                      PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder() -                            .build().get(HashAlgorithmTags.SHA1); +                            .build().get(PgpConstants.SECRET_KEY_SIGNATURE_CHECKSUM_HASH_ALGO);                      sKey = new PGPSecretKey(keyPair.getPrivateKey(), pKey, sha1Calc, false, keyEncryptor);                  } @@ -1025,7 +981,8 @@ public class PgpKeyOperation {                  // add packet with EMPTY notation data (updates old one, but will be stripped later)                  PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( -                        masterPrivateKey.getPublicKeyPacket().getAlgorithm(), HashAlgorithmTags.SHA512) +                        masterPrivateKey.getPublicKeyPacket().getAlgorithm(), +                        PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO)                          .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);                  PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);                  { // set subpackets @@ -1051,7 +1008,8 @@ public class PgpKeyOperation {              // add packet with "pin" notation data              PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( -                    masterPrivateKey.getPublicKeyPacket().getAlgorithm(), HashAlgorithmTags.SHA512) +                    masterPrivateKey.getPublicKeyPacket().getAlgorithm(), +                    PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO)                      .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);              PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);              { // set subpackets @@ -1098,12 +1056,13 @@ public class PgpKeyOperation {              OperationLog log, int indent) throws PGPException {          PGPDigestCalculator encryptorHashCalc = new JcaPGPDigestCalculatorProviderBuilder().build() -                .get(SECRET_KEY_ENCRYPTOR_HASH_ALGO); +                .get(PgpConstants.SECRET_KEY_ENCRYPTOR_HASH_ALGO);          PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(                  Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());          // Build key encryptor based on new passphrase          PBESecretKeyEncryptor keyEncryptorNew = new JcePBESecretKeyEncryptorBuilder( -                SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc, SECRET_KEY_ENCRYPTOR_S2K_COUNT) +                PgpConstants.SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc, +                PgpConstants.SECRET_KEY_ENCRYPTOR_S2K_COUNT)                  .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(                          newPassphrase.toCharArray()); @@ -1236,7 +1195,8 @@ public class PgpKeyOperation {              int flags, long expiry)              throws IOException, PGPException, SignatureException {          PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( -                masterPrivateKey.getPublicKeyPacket().getAlgorithm(), HashAlgorithmTags.SHA512) +                masterPrivateKey.getPublicKeyPacket().getAlgorithm(), +                PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO)                  .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);          PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder); @@ -1253,9 +1213,12 @@ public class PgpKeyOperation {               * error than be ignored.               */              /* non-critical subpackets: */ -            hashedPacketsGen.setPreferredSymmetricAlgorithms(false, PREFERRED_SYMMETRIC_ALGORITHMS); -            hashedPacketsGen.setPreferredHashAlgorithms(false, PREFERRED_HASH_ALGORITHMS); -            hashedPacketsGen.setPreferredCompressionAlgorithms(false, PREFERRED_COMPRESSION_ALGORITHMS); +            hashedPacketsGen.setPreferredSymmetricAlgorithms(false, +                    PgpConstants.getAsArray(PgpConstants.sPreferredSymmetricAlgorithms)); +            hashedPacketsGen.setPreferredHashAlgorithms(false, +                    PgpConstants.getAsArray(PgpConstants.sPreferredHashAlgorithms)); +            hashedPacketsGen.setPreferredCompressionAlgorithms(false, +                    PgpConstants.getAsArray(PgpConstants.sPreferredCompressionAlgorithms));              hashedPacketsGen.setPrimaryUserID(false, primary);              /* critical subpackets: we consider those important for a modern pgp implementation */ @@ -1279,7 +1242,8 @@ public class PgpKeyOperation {              PGPUserAttributeSubpacketVector vector)                  throws IOException, PGPException, SignatureException {          PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( -                masterPrivateKey.getPublicKeyPacket().getAlgorithm(), HashAlgorithmTags.SHA512) +                masterPrivateKey.getPublicKeyPacket().getAlgorithm(), +                PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO)                  .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);          PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder); @@ -1298,7 +1262,8 @@ public class PgpKeyOperation {              PGPPrivateKey masterPrivateKey, PGPPublicKey pKey, String userId)          throws IOException, PGPException, SignatureException {          PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( -                masterPrivateKey.getPublicKeyPacket().getAlgorithm(), HashAlgorithmTags.SHA512) +                masterPrivateKey.getPublicKeyPacket().getAlgorithm(), +                PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO)                  .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);          PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);          PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator(); @@ -1312,7 +1277,7 @@ public class PgpKeyOperation {              PGPPublicKey masterPublicKey, PGPPrivateKey masterPrivateKey, PGPPublicKey pKey)              throws IOException, PGPException, SignatureException {          PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( -                masterPublicKey.getAlgorithm(), HashAlgorithmTags.SHA512) +                masterPublicKey.getAlgorithm(), PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO)                  .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);          PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);          PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator(); @@ -1356,7 +1321,7 @@ public class PgpKeyOperation {              PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator();              subHashedPacketsGen.setSignatureCreationTime(false, creationTime);              PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( -                    pKey.getAlgorithm(), HashAlgorithmTags.SHA512) +                    pKey.getAlgorithm(), PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO)                      .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);              PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);              sGen.init(PGPSignature.PRIMARYKEY_BINDING, subPrivateKey); @@ -1377,7 +1342,7 @@ public class PgpKeyOperation {          }          PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( -                masterPublicKey.getAlgorithm(), HashAlgorithmTags.SHA512) +                masterPublicKey.getAlgorithm(), PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO)                  .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);          PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);          sGen.init(PGPSignature.SUBKEY_BINDING, masterPrivateKey); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptInput.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptInput.java index 9318be006..1ed0a4720 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptInput.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptInput.java @@ -12,10 +12,10 @@ public class PgpSignEncryptInput {      protected int mCompressionId = CompressionAlgorithmTags.UNCOMPRESSED;      protected long[] mEncryptionMasterKeyIds = null;      protected String mSymmetricPassphrase = null; -    protected int mSymmetricEncryptionAlgorithm = 0; +    protected int mSymmetricEncryptionAlgorithm = PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED;      protected long mSignatureMasterKeyId = Constants.key.none;      protected Long mSignatureSubKeyId = null; -    protected int mSignatureHashAlgorithm = 0; +    protected int mSignatureHashAlgorithm = PgpConstants.OpenKeychainHashAlgorithmTags.USE_PREFERRED;      protected String mSignaturePassphrase = null;      protected long mAdditionalEncryptId = Constants.key.none;      protected byte[] mNfcSignedHash = null; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java index 2fa01d241..81cc2c847 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java @@ -25,7 +25,6 @@ import org.spongycastle.bcpg.ArmoredOutputStream;  import org.spongycastle.bcpg.BCPGOutputStream;  import org.spongycastle.bcpg.CompressionAlgorithmTags;  import org.spongycastle.openpgp.PGPCompressedDataGenerator; -import org.spongycastle.openpgp.PGPEncryptedData;  import org.spongycastle.openpgp.PGPEncryptedDataGenerator;  import org.spongycastle.openpgp.PGPException;  import org.spongycastle.openpgp.PGPLiteralData; @@ -58,6 +57,7 @@ import java.io.InputStreamReader;  import java.io.OutputStream;  import java.io.UnsupportedEncodingException;  import java.security.SignatureException; +import java.util.ArrayList;  import java.util.Arrays;  import java.util.Date;  import java.util.LinkedList; @@ -206,12 +206,12 @@ public class PgpSignEncryptOperation extends BaseOperation {                  return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);              } -            // check if hash algo is supported +            // Use preferred hash algo              int requestedAlgorithm = input.getSignatureHashAlgorithm(); -            LinkedList<Integer> supported = signingKey.getSupportedHashAlgorithms(); -            if (requestedAlgorithm == 0) { +            ArrayList<Integer> supported = signingKey.getSupportedHashAlgorithms(); +            if (requestedAlgorithm == PgpConstants.OpenKeychainHashAlgorithmTags.USE_PREFERRED) {                  // get most preferred -                input.setSignatureHashAlgorithm(supported.getLast()); +                input.setSignatureHashAlgorithm(supported.get(0));              } else if (!supported.contains(requestedAlgorithm)) {                  log.add(LogType.MSG_PSE_ERROR_HASH_ALGO, indent);                  return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log); @@ -222,9 +222,13 @@ public class PgpSignEncryptOperation extends BaseOperation {          /* Initialize PGPEncryptedDataGenerator for later usage */          PGPEncryptedDataGenerator cPk = null;          if (enableEncryption) { + +            // Use preferred encryption algo              int algo = input.getSymmetricEncryptionAlgorithm(); -            if (algo == 0) { -                algo = PGPEncryptedData.AES_128; +            if (algo == PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED) { +                // get most preferred +                // TODO: get from recipients +                algo = PgpConstants.sPreferredSymmetricAlgorithms.get(0);              }              // has Integrity packet enabled!              JcePGPDataEncryptorBuilder encryptorBuilder = diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java index 2601c1f69..4ccfc3cd9 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -650,7 +650,7 @@ public class KeychainProvider extends ContentProvider {              cursor.setNotificationUri(getContext().getContentResolver(), uri);          } -        if (Constants.DEBUG) { +        if (Constants.DEBUG && Constants.DEBUG_LOG_DB_QUERIES) {              Log.d(Constants.TAG,                      "Query: "                              + qb.buildQuery(projection, selection, selectionArgs, null, null, diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index d947ae053..6366e8536 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -1372,8 +1372,7 @@ public class ProviderHelper {          UncachedKeyRing keyRing = UncachedKeyRing.decodeFromData(data);          ByteArrayOutputStream bos = new ByteArrayOutputStream(); -        String version = PgpHelper.getVersionForHeader(mContext); -        keyRing.encodeArmored(bos, version); +        keyRing.encodeArmored(bos, null);          String armoredKey = bos.toString("UTF-8");          Log.d(Constants.TAG, "armoredKey:" + armoredKey); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java index 1a65b1bee..03fa41984 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java @@ -30,13 +30,16 @@ import org.openintents.openpgp.OpenPgpError;  import org.openintents.openpgp.OpenPgpMetadata;  import org.openintents.openpgp.OpenPgpSignatureResult;  import org.openintents.openpgp.util.OpenPgpApi; +import org.spongycastle.bcpg.CompressionAlgorithmTags;  import org.spongycastle.util.encoders.Hex;  import org.sufficientlysecure.keychain.Constants;  import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;  import org.sufficientlysecure.keychain.operations.results.OperationResult.LogEntryParcel;  import org.sufficientlysecure.keychain.operations.results.PgpSignEncryptResult; +import org.sufficientlysecure.keychain.pgp.PgpConstants;  import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;  import org.sufficientlysecure.keychain.pgp.PgpHelper; +import org.sufficientlysecure.keychain.pgp.PgpKeyOperation;  import org.sufficientlysecure.keychain.pgp.PgpSignEncryptInput;  import org.sufficientlysecure.keychain.pgp.PgpSignEncryptOperation;  import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; @@ -52,6 +55,7 @@ import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity;  import org.sufficientlysecure.keychain.ui.ViewKeyActivity;  import org.sufficientlysecure.keychain.util.InputData;  import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Preferences;  import java.io.IOException;  import java.io.InputStream; @@ -258,8 +262,8 @@ public class OpenPgpService extends RemoteService {                      .setEnableAsciiArmorOutput(asciiArmor)                      .setCleartextSignature(cleartextSign)                      .setDetachedSignature(!cleartextSign) -                    .setVersionHeader(PgpHelper.getVersionForHeader(this)) -                    .setSignatureHashAlgorithm(accSettings.getHashAlgorithm()) +                    .setVersionHeader(null) +                    .setSignatureHashAlgorithm(PgpConstants.OpenKeychainHashAlgorithmTags.USE_PREFERRED)                      .setSignatureMasterKeyId(accSettings.getKeyId())                      .setNfcState(nfcSignedHash, nfcCreationDate); @@ -356,9 +360,9 @@ public class OpenPgpService extends RemoteService {              PgpSignEncryptInput pseInput = new PgpSignEncryptInput();              pseInput.setEnableAsciiArmorOutput(asciiArmor) -                    .setVersionHeader(PgpHelper.getVersionForHeader(this)) -                    .setCompressionId(accSettings.getCompression()) -                    .setSymmetricEncryptionAlgorithm(accSettings.getEncryptionAlgorithm()) +                    .setVersionHeader(null) +                    .setCompressionId(CompressionAlgorithmTags.UNCOMPRESSED) +                    .setSymmetricEncryptionAlgorithm(PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED)                      .setEncryptionMasterKeyIds(keyIds)                      .setFailOnMissingEncryptionKeyIds(true)                      .setAdditionalEncryptId(accSettings.getKeyId()); // add acc key for encryption @@ -374,7 +378,7 @@ public class OpenPgpService extends RemoteService {                  }                  // sign and encrypt -                pseInput.setSignatureHashAlgorithm(accSettings.getHashAlgorithm()) +                pseInput.setSignatureHashAlgorithm(PgpConstants.OpenKeychainHashAlgorithmTags.USE_PREFERRED)                          .setSignatureMasterKeyId(accSettings.getKeyId())                          .setNfcState(nfcSignedHash, nfcCreationDate);              } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java index 62c38d136..60cc404b6 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java @@ -28,9 +28,11 @@ public class CreateKeyActivity extends BaseActivity {      public static final String EXTRA_NAME = "name";      public static final String EXTRA_EMAIL = "email"; -    public static final int FRAG_ACTION_START = 0; -    public static final int FRAG_ACTION_TO_RIGHT = 1; -    public static final int FRAG_ACTION_TO_LEFT = 2; +    public static enum FragAction { +        START, +        TO_RIGHT, +        TO_LEFT +    }      @Override      public void onCreate(Bundle savedInstanceState) { @@ -42,7 +44,7 @@ public class CreateKeyActivity extends BaseActivity {                          getIntent().getStringExtra(EXTRA_NAME),                          getIntent().getStringExtra(EXTRA_EMAIL)                  ); -        loadFragment(null, frag, FRAG_ACTION_START); +        loadFragment(null, frag, FragAction.START);      }      @Override @@ -50,7 +52,7 @@ public class CreateKeyActivity extends BaseActivity {          setContentView(R.layout.create_key_activity);      } -    public void loadFragment(Bundle savedInstanceState, Fragment fragment, int action) { +    public void loadFragment(Bundle savedInstanceState, Fragment fragment, FragAction action) {          // However, if we're being restored from a previous state,          // then we don't need to do anything and should return or else          // we could end up with overlapping fragments. @@ -63,15 +65,15 @@ public class CreateKeyActivity extends BaseActivity {          FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();          switch (action) { -            case FRAG_ACTION_START: +            case START:                  transaction.setCustomAnimations(0, 0);                  transaction.replace(R.id.create_key_fragment_container, fragment)                          .commitAllowingStateLoss();                  break; -            case FRAG_ACTION_TO_LEFT: +            case TO_LEFT:                  getSupportFragmentManager().popBackStackImmediate();                  break; -            case FRAG_ACTION_TO_RIGHT: +            case TO_RIGHT:                  transaction.setCustomAnimations(R.anim.frag_slide_in_from_right, R.anim.frag_slide_out_to_left,                          R.anim.frag_slide_in_from_left, R.anim.frag_slide_out_to_right);                  transaction.addToBackStack(null); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java index 6e0115342..920488e3e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java @@ -43,6 +43,7 @@ import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;  import org.sufficientlysecure.keychain.service.SaveKeyringParcel;  import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;  import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel; +import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction;  import org.sufficientlysecure.keychain.util.Log;  import org.sufficientlysecure.keychain.util.Preferences; @@ -117,7 +118,7 @@ public class CreateKeyFinalFragment extends Fragment {          mBackButton.setOnClickListener(new View.OnClickListener() {              @Override              public void onClick(View v) { -                mCreateKeyActivity.loadFragment(null, null, CreateKeyActivity.FRAG_ACTION_TO_LEFT); +                mCreateKeyActivity.loadFragment(null, null, FragAction.TO_LEFT);              }          }); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyInputFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyInputFragment.java index 091418304..115614808 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyInputFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyInputFragment.java @@ -32,6 +32,7 @@ import android.widget.AutoCompleteTextView;  import android.widget.EditText;  import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction;  import org.sufficientlysecure.keychain.ui.widget.passwordstrengthindicator.PasswordStrengthView;  import org.sufficientlysecure.keychain.util.ContactHelper; @@ -187,7 +188,7 @@ public class CreateKeyInputFragment extends Fragment {                      );              hideKeyboard(); -            mCreateKeyActivity.loadFragment(null, frag, CreateKeyActivity.FRAG_ACTION_TO_RIGHT); +            mCreateKeyActivity.loadFragment(null, frag, FragAction.TO_RIGHT);          }      } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java index 8723c7255..60103f344 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java @@ -31,6 +31,7 @@ import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;  import org.sufficientlysecure.keychain.pgp.KeyRing;  import org.sufficientlysecure.keychain.provider.KeychainContract;  import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State;  public abstract class DecryptFragment extends Fragment {      private static final int RESULT_CODE_LOOKUP_KEY = 0x00007006; @@ -141,16 +142,16 @@ public abstract class DecryptFragment extends Fragment {              if (signatureResult.isSignatureOnly()) {                  mEncryptionText.setText(R.string.decrypt_result_not_encrypted); -                KeyFormattingUtils.setStatusImage(getActivity(), mEncryptionIcon, mEncryptionText, KeyFormattingUtils.STATE_NOT_ENCRYPTED); +                KeyFormattingUtils.setStatusImage(getActivity(), mEncryptionIcon, mEncryptionText, State.NOT_ENCRYPTED);              } else {                  mEncryptionText.setText(R.string.decrypt_result_encrypted); -                KeyFormattingUtils.setStatusImage(getActivity(), mEncryptionIcon, mEncryptionText, KeyFormattingUtils.STATE_ENCRYPTED); +                KeyFormattingUtils.setStatusImage(getActivity(), mEncryptionIcon, mEncryptionText, State.ENCRYPTED);              }              switch (signatureResult.getStatus()) {                  case OpenPgpSignatureResult.SIGNATURE_SUCCESS_CERTIFIED: {                      mSignatureText.setText(R.string.decrypt_result_signature_certified); -                    KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, KeyFormattingUtils.STATE_VERIFIED); +                    KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, State.VERIFIED);                      setSignatureLayoutVisibility(View.VISIBLE);                      setShowAction(mSignatureKeyId); @@ -161,7 +162,7 @@ public abstract class DecryptFragment extends Fragment {                  case OpenPgpSignatureResult.SIGNATURE_SUCCESS_UNCERTIFIED: {                      mSignatureText.setText(R.string.decrypt_result_signature_uncertified); -                    KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, KeyFormattingUtils.STATE_UNVERIFIED); +                    KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, State.UNVERIFIED);                      setSignatureLayoutVisibility(View.VISIBLE);                      setShowAction(mSignatureKeyId); @@ -172,11 +173,11 @@ public abstract class DecryptFragment extends Fragment {                  case OpenPgpSignatureResult.SIGNATURE_KEY_MISSING: {                      mSignatureText.setText(R.string.decrypt_result_signature_missing_key); -                    KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, KeyFormattingUtils.STATE_UNKNOWN_KEY); +                    KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, State.UNKNOWN_KEY);                      setSignatureLayoutVisibility(View.VISIBLE);                      mSignatureAction.setText(R.string.decrypt_result_action_Lookup); -                    mSignatureAction.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_action_download, 0); +                    mSignatureAction.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_file_download_grey_24dp, 0);                      mSignatureLayout.setOnClickListener(new View.OnClickListener() {                          @Override                          public void onClick(View v) { @@ -190,7 +191,7 @@ public abstract class DecryptFragment extends Fragment {                  case OpenPgpSignatureResult.SIGNATURE_KEY_EXPIRED: {                      mSignatureText.setText(R.string.decrypt_result_signature_expired_key); -                    KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, KeyFormattingUtils.STATE_EXPIRED); +                    KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, State.EXPIRED);                      setSignatureLayoutVisibility(View.VISIBLE);                      setShowAction(mSignatureKeyId); @@ -201,7 +202,7 @@ public abstract class DecryptFragment extends Fragment {                  case OpenPgpSignatureResult.SIGNATURE_KEY_REVOKED: {                      mSignatureText.setText(R.string.decrypt_result_signature_revoked_key); -                    KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, KeyFormattingUtils.STATE_REVOKED); +                    KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, State.REVOKED);                      setSignatureLayoutVisibility(View.VISIBLE);                      setShowAction(mSignatureKeyId); @@ -212,7 +213,7 @@ public abstract class DecryptFragment extends Fragment {                  case OpenPgpSignatureResult.SIGNATURE_ERROR: {                      mSignatureText.setText(R.string.decrypt_result_invalid_signature); -                    KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, KeyFormattingUtils.STATE_INVALID); +                    KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, State.INVALID);                      setSignatureLayoutVisibility(View.GONE); @@ -224,9 +225,9 @@ public abstract class DecryptFragment extends Fragment {              setSignatureLayoutVisibility(View.GONE);              mSignatureText.setText(R.string.decrypt_result_no_signature); -            KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, KeyFormattingUtils.STATE_NOT_SIGNED); +            KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, State.NOT_SIGNED);              mEncryptionText.setText(R.string.decrypt_result_encrypted); -            KeyFormattingUtils.setStatusImage(getActivity(), mEncryptionIcon, mEncryptionText, KeyFormattingUtils.STATE_ENCRYPTED); +            KeyFormattingUtils.setStatusImage(getActivity(), mEncryptionIcon, mEncryptionText, State.ENCRYPTED);              valid = true;          } @@ -242,7 +243,7 @@ public abstract class DecryptFragment extends Fragment {      private void setShowAction(final long signatureKeyId) {          mSignatureAction.setText(R.string.decrypt_result_action_show); -        mSignatureAction.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_action_accounts, 0); +        mSignatureAction.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_vpn_key_grey_24dp, 0);          mSignatureLayout.setOnClickListener(new View.OnClickListener() {              @Override              public void onClick(View v) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivityInterface.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivityInterface.java index 54fe369a7..baf445293 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivityInterface.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivityInterface.java @@ -28,6 +28,7 @@ public interface EncryptActivityInterface {      }      public boolean isUseArmor(); +    public boolean isUseCompression();      public long getSignatureKey();      public long[] getEncryptionKeys(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesActivity.java index 1286617d3..11b596c24 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesActivity.java @@ -25,11 +25,13 @@ import android.support.v4.app.Fragment;  import android.view.Menu;  import android.view.MenuItem; +import org.spongycastle.bcpg.CompressionAlgorithmTags;  import org.sufficientlysecure.keychain.Constants;  import org.sufficientlysecure.keychain.R;  import org.sufficientlysecure.keychain.api.OpenKeychainIntents;  import org.sufficientlysecure.keychain.operations.results.SignEncryptResult;  import org.sufficientlysecure.keychain.pgp.KeyRing; +import org.sufficientlysecure.keychain.pgp.PgpConstants;  import org.sufficientlysecure.keychain.pgp.SignEncryptParcel;  import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment;  import org.sufficientlysecure.keychain.ui.util.Notify; @@ -66,6 +68,7 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi      private long mSigningKeyId = Constants.key.none;      private String mPassphrase = "";      private boolean mUseArmor; +    private boolean mUseCompression;      private boolean mDeleteAfterEncrypt = false;      private boolean mShareAfterEncrypt = false;      private ArrayList<Uri> mInputUris; @@ -82,6 +85,11 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi      }      @Override +    public boolean isUseCompression() { +        return mUseCompression; +    } + +    @Override      public long getSignatureKey() {          return mSigningKeyId;      } @@ -196,10 +204,13 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi          data.addInputUris(mInputUris);          data.addOutputUris(mOutputUris); -        data.setCompressionId(Preferences.getPreferences(this).getDefaultMessageCompression()); - -        // Always use armor for messages -        data.setEnableAsciiArmorOutput(mUseArmor); +        if (mUseCompression) { +            data.setCompressionId(PgpConstants.sPreferredCompressionAlgorithms.get(0)); +        } else { +            data.setCompressionId(CompressionAlgorithmTags.UNCOMPRESSED); +        } +        data.setSymmetricEncryptionAlgorithm(PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED); +        data.setSignatureHashAlgorithm(PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED);          if (isModeSymmetric()) {              Log.d(Constants.TAG, "Symmetric encryption enabled!"); @@ -315,8 +326,6 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi          // Handle intent actions          handleActions(getIntent());          updateModeFragment(); - -        mUseArmor = Preferences.getPreferences(this).getDefaultAsciiArmor();      }      @Override @@ -327,7 +336,6 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi      @Override      public boolean onCreateOptionsMenu(Menu menu) {          getMenuInflater().inflate(R.menu.encrypt_file_activity, menu); -        menu.findItem(R.id.check_use_armor).setChecked(mUseArmor);          return super.onCreateOptionsMenu(menu);      } @@ -348,21 +356,30 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi              item.setChecked(!item.isChecked());          }          switch (item.getItemId()) { -            case R.id.check_use_symmetric: +            case R.id.check_use_symmetric: {                  mCurrentMode = item.isChecked() ? MODE_SYMMETRIC : MODE_ASYMMETRIC;                  updateModeFragment();                  notifyUpdate();                  break; -            case R.id.check_use_armor: +            } +            case R.id.check_use_armor: {                  mUseArmor = item.isChecked();                  notifyUpdate();                  break; -            case R.id.check_delete_after_encrypt: +            } +            case R.id.check_delete_after_encrypt: {                  mDeleteAfterEncrypt = item.isChecked();                  notifyUpdate();                  break; -            default: +            } +            case R.id.check_enable_compression: { +                mUseCompression = item.isChecked(); +                notifyUpdate(); +                break; +            } +            default: {                  return super.onOptionsItemSelected(item); +            }          }          return true;      } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java index 2dd861d07..08ff5b962 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java @@ -25,12 +25,14 @@ import android.support.v4.app.Fragment;  import android.view.Menu;  import android.view.MenuItem; +import org.spongycastle.bcpg.CompressionAlgorithmTags;  import org.sufficientlysecure.keychain.Constants;  import org.sufficientlysecure.keychain.R;  import org.sufficientlysecure.keychain.api.OpenKeychainIntents;  import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;  import org.sufficientlysecure.keychain.operations.results.SignEncryptResult;  import org.sufficientlysecure.keychain.pgp.KeyRing; +import org.sufficientlysecure.keychain.pgp.PgpConstants;  import org.sufficientlysecure.keychain.pgp.SignEncryptParcel;  import org.sufficientlysecure.keychain.ui.util.Notify;  import org.sufficientlysecure.keychain.util.Log; @@ -70,6 +72,7 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv      private ArrayList<Uri> mInputUris;      private ArrayList<Uri> mOutputUris;      private String mMessage = ""; +    private boolean mUseCompression;      public boolean isModeSymmetric() {          return MODE_SYMMETRIC == mCurrentMode; @@ -81,6 +84,11 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv      }      @Override +    public boolean isUseCompression() { +        return mUseCompression; +    } + +    @Override      public long getSignatureKey() {          return mSigningKeyId;      } @@ -189,7 +197,13 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv          data.setBytes(mMessage.getBytes());          data.setCleartextSignature(true); -        data.setCompressionId(Preferences.getPreferences(this).getDefaultMessageCompression()); +        if (mUseCompression) { +            data.setCompressionId(PgpConstants.sPreferredCompressionAlgorithms.get(0)); +        } else { +            data.setCompressionId(CompressionAlgorithmTags.UNCOMPRESSED); +        } +        data.setSymmetricEncryptionAlgorithm(PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED); +        data.setSignatureHashAlgorithm(PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED);          // Always use armor for messages          data.setEnableAsciiArmorOutput(true); @@ -328,13 +342,20 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv              item.setChecked(!item.isChecked());          }          switch (item.getItemId()) { -            case R.id.check_use_symmetric: +            case R.id.check_use_symmetric: {                  mCurrentMode = item.isChecked() ? MODE_SYMMETRIC : MODE_ASYMMETRIC;                  updateModeFragment();                  notifyUpdate();                  break; -            default: +            } +            case R.id.check_enable_compression: { +                mUseCompression = item.isChecked(); +                notifyUpdate(); +                break; +            } +            default: {                  return super.onOptionsItemSelected(item); +            }          }          return true;      } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpActivity.java index 2eb35351e..cd6cdf4d6 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpActivity.java @@ -32,10 +32,9 @@ public class HelpActivity extends BaseActivity {      public static final int TAB_START = 0;      public static final int TAB_FAQ = 1; -    public static final int TAB_WOT = 2; -    public static final int TAB_NFC = 3; -    public static final int TAB_CHANGELOG = 4; -    public static final int TAB_ABOUT = 5; +    public static final int TAB_TRUST = 2; +    public static final int TAB_CHANGELOG = 3; +    public static final int TAB_ABOUT = 4;      ViewPager mViewPager;      private PagerTabStripAdapter mTabsAdapter; @@ -69,21 +68,11 @@ public class HelpActivity extends BaseActivity {          mTabsAdapter.addTab(HelpHtmlFragment.class, startBundle,                  getString(R.string.help_tab_start)); -        Bundle faqBundle = new Bundle(); -        faqBundle.putInt(HelpHtmlFragment.ARG_HTML_FILE, R.raw.help_faq); -        mTabsAdapter.addTab(HelpHtmlFragment.class, faqBundle, -                getString(R.string.help_tab_faq)); -          Bundle wotBundle = new Bundle(); -        wotBundle.putInt(HelpHtmlFragment.ARG_HTML_FILE, R.raw.help_wot); +        wotBundle.putInt(HelpHtmlFragment.ARG_HTML_FILE, R.raw.help_certification);          mTabsAdapter.addTab(HelpHtmlFragment.class, wotBundle,                  getString(R.string.help_tab_wot)); -        Bundle nfcBundle = new Bundle(); -        nfcBundle.putInt(HelpHtmlFragment.ARG_HTML_FILE, R.raw.help_nfc_beam); -        mTabsAdapter.addTab(HelpHtmlFragment.class, nfcBundle, -                getString(R.string.help_tab_nfc_beam)); -          Bundle changelogBundle = new Bundle();          changelogBundle.putInt(HelpHtmlFragment.ARG_HTML_FILE, R.raw.help_changelog);          mTabsAdapter.addTab(HelpHtmlFragment.class, changelogBundle, diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java index b56da463a..43d893fa6 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java @@ -70,6 +70,7 @@ import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;  import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;  import org.sufficientlysecure.keychain.ui.util.Highlighter;  import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State;  import org.sufficientlysecure.keychain.ui.util.Notify;  import org.sufficientlysecure.keychain.util.ExportHelper;  import org.sufficientlysecure.keychain.util.FabContainer; @@ -77,7 +78,6 @@ import org.sufficientlysecure.keychain.util.Log;  import org.sufficientlysecure.keychain.util.Preferences;  import java.io.IOException; -import java.util.Date;  import java.util.HashMap;  import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter; @@ -268,7 +268,7 @@ public class KeyListFragment extends LoaderFragment              KeyRings.MASTER_KEY_ID,              KeyRings.USER_ID,              KeyRings.IS_REVOKED, -            KeyRings.EXPIRY, +            KeyRings.IS_EXPIRED,              KeyRings.VERIFIED,              KeyRings.HAS_ANY_SECRET      }; @@ -276,7 +276,7 @@ public class KeyListFragment extends LoaderFragment      static final int INDEX_MASTER_KEY_ID = 1;      static final int INDEX_USER_ID = 2;      static final int INDEX_IS_REVOKED = 3; -    static final int INDEX_EXPIRY = 4; +    static final int INDEX_IS_EXPIRED = 4;      static final int INDEX_VERIFIED = 5;      static final int INDEX_HAS_ANY_SECRET = 6; @@ -678,9 +678,6 @@ public class KeyListFragment extends LoaderFragment          /**           * Bind cursor data to the item list view -         * <p/> -         * NOTE: CursorAdapter already implements the ViewHolder pattern in its getView() method. -         * Thus no ViewHolder is required here.           */          @Override          public void bindView(View view, Context context, Cursor cursor) { @@ -708,21 +705,20 @@ public class KeyListFragment extends LoaderFragment                  long masterKeyId = cursor.getLong(INDEX_MASTER_KEY_ID);                  boolean isSecret = cursor.getInt(INDEX_HAS_ANY_SECRET) != 0;                  boolean isRevoked = cursor.getInt(INDEX_IS_REVOKED) > 0; -                boolean isExpired = !cursor.isNull(INDEX_EXPIRY) -                        && new Date(cursor.getLong(INDEX_EXPIRY) * 1000).before(new Date()); +                boolean isExpired = cursor.getInt(INDEX_IS_EXPIRED) != 0;                  boolean isVerified = cursor.getInt(INDEX_VERIFIED) > 0;                  h.mMasterKeyId = masterKeyId;                  // Note: order is important!                  if (isRevoked) { -                    KeyFormattingUtils.setStatusImage(getActivity(), h.mStatus, null, KeyFormattingUtils.STATE_REVOKED, R.color.bg_gray); +                    KeyFormattingUtils.setStatusImage(getActivity(), h.mStatus, null, State.REVOKED, R.color.bg_gray);                      h.mStatus.setVisibility(View.VISIBLE);                      h.mSlinger.setVisibility(View.GONE);                      h.mMainUserId.setTextColor(context.getResources().getColor(R.color.bg_gray));                      h.mMainUserIdRest.setTextColor(context.getResources().getColor(R.color.bg_gray));                  } else if (isExpired) { -                    KeyFormattingUtils.setStatusImage(getActivity(), h.mStatus, null, KeyFormattingUtils.STATE_EXPIRED, R.color.bg_gray); +                    KeyFormattingUtils.setStatusImage(getActivity(), h.mStatus, null, State.EXPIRED, R.color.bg_gray);                      h.mStatus.setVisibility(View.VISIBLE);                      h.mSlinger.setVisibility(View.GONE);                      h.mMainUserId.setTextColor(context.getResources().getColor(R.color.bg_gray)); @@ -735,10 +731,10 @@ public class KeyListFragment extends LoaderFragment                  } else {                      // this is a public key - show if it's verified                      if (isVerified) { -                        KeyFormattingUtils.setStatusImage(getActivity(), h.mStatus, KeyFormattingUtils.STATE_VERIFIED); +                        KeyFormattingUtils.setStatusImage(getActivity(), h.mStatus, State.VERIFIED);                          h.mStatus.setVisibility(View.VISIBLE);                      } else { -                        KeyFormattingUtils.setStatusImage(getActivity(), h.mStatus, KeyFormattingUtils.STATE_UNVERIFIED); +                        KeyFormattingUtils.setStatusImage(getActivity(), h.mStatus, State.UNVERIFIED);                          h.mStatus.setVisibility(View.VISIBLE);                      }                      h.mSlinger.setVisibility(View.GONE); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java index b655a7e55..138f2f4e7 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java @@ -22,9 +22,13 @@ import android.content.Context;  import android.content.Intent;  import android.graphics.Color;  import android.os.Bundle; +import android.os.Parcel;  import android.support.v4.app.ListFragment;  import android.util.TypedValue;  import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem;  import android.view.View;  import android.view.ViewGroup;  import android.widget.AdapterView; @@ -33,11 +37,19 @@ import android.widget.ArrayAdapter;  import android.widget.ImageView;  import android.widget.TextView; +import org.sufficientlysecure.keychain.Constants;  import org.sufficientlysecure.keychain.R;  import org.sufficientlysecure.keychain.operations.results.OperationResult;  import org.sufficientlysecure.keychain.operations.results.OperationResult.LogEntryParcel;  import org.sufficientlysecure.keychain.operations.results.OperationResult.LogLevel;  import org.sufficientlysecure.keychain.operations.results.OperationResult.SubLogEntryParcel; +import org.sufficientlysecure.keychain.util.FileHelper; +import org.sufficientlysecure.keychain.util.Log; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.PrintWriter; +import java.util.Iterator;  public class LogDisplayFragment extends ListFragment implements OnItemClickListener { @@ -46,6 +58,12 @@ public class LogDisplayFragment extends ListFragment implements OnItemClickListe      OperationResult mResult;      public static final String EXTRA_RESULT = "log"; +    @Override +    public void onCreate(Bundle savedInstanceState) { +        super.onCreate(savedInstanceState); + +        setHasOptionsMenu(true); +    }      @Override      public void onActivityCreated(Bundle savedInstanceState) { @@ -70,6 +88,183 @@ public class LogDisplayFragment extends ListFragment implements OnItemClickListe          getListView().setFastScrollEnabled(true);          getListView().setDividerHeight(0); + +    } + +    @Override +    public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) { +        inflater.inflate(R.menu.log_display, menu); + +        super.onCreateOptionsMenu(menu, inflater); +    } + +    @Override +    public boolean onOptionsItemSelected(MenuItem item) { +        switch (item.getItemId()) { +            case R.id.menu_log_display_export_log: +                exportLog(); +                break; +        } + +        return super.onOptionsItemSelected(item); +    } + +    private void exportLog() { + +        showExportLogDialog(new File(Constants.Path.APP_DIR, "export.log")); +    } + +    private void writeToLogFile(final OperationResult.OperationLog operationLog, final File f) { +        OperationResult.OperationLog currLog = new OperationResult.OperationLog(); +        currLog.add(OperationResult.LogType.MSG_EXPORT_LOG, 0); + +        boolean error = false; + +        PrintWriter pw = null; +        try { +            pw = new PrintWriter(f); +            pw.print(getPrintableOperationLog(operationLog, "")); +            if (pw.checkError()) {//IOException +                Log.e(Constants.TAG, "Log Export I/O Exception " + f.getAbsolutePath()); +                currLog.add(OperationResult.LogType.MSG_EXPORT_LOG_EXPORT_ERROR_WRITING, 1); +                error = true; +            } +        } catch (FileNotFoundException e) { +            Log.e(Constants.TAG, "File not found for exporting log " + f.getAbsolutePath()); +            currLog.add(OperationResult.LogType.MSG_EXPORT_LOG_EXPORT_ERROR_FOPEN, 1); +            error = true; +        } +        if (pw != null) { +            pw.close(); +            if (!error && pw.checkError()) {//check if it is only pw.close() which generated error +                currLog.add(OperationResult.LogType.MSG_EXPORT_LOG_EXPORT_ERROR_WRITING, 1); +                error = true; +            } +        } + +        if (!error) currLog.add(OperationResult.LogType.MSG_EXPORT_LOG_EXPORT_SUCCESS, 1); + +        int opResultCode = error ? OperationResult.RESULT_ERROR : OperationResult.RESULT_OK; +        OperationResult opResult = new LogExportResult(opResultCode, currLog); +        opResult.createNotify(getActivity()).show(); +    } + +    /** +     * returns an indented String of an entire OperationLog +     * +     * @param opLog       log to be converted to indented, printable format +     * @param basePadding padding to add at the start of all log entries, made for use with SubLogs +     * @return printable, indented version of passed operationLog +     */ +    private String getPrintableOperationLog(OperationResult.OperationLog opLog, String basePadding) { +        String log = ""; +        for (Iterator<LogEntryParcel> logIterator = opLog.iterator(); logIterator.hasNext(); ) { +            log += getPrintableLogEntry(logIterator.next(), basePadding) + "\n"; +        } +        log = log.substring(0, log.length() - 1);//gets rid of extra new line +        return log; +    } + +    /** +     * returns an indented String of a LogEntryParcel including any sub-logs it may contain +     * +     * @param entryParcel log entryParcel whose String representation is to be obtained +     * @return indented version of passed log entryParcel in a readable format +     */ +    private String getPrintableLogEntry(OperationResult.LogEntryParcel entryParcel, +                                        String basePadding) { + +        final String indent = "    ";//4 spaces = 1 Indent level + +        String padding = basePadding; +        for (int i = 0; i < entryParcel.mIndent; i++) { +            padding += indent; +        } +        String logText = padding; + +        switch (entryParcel.mType.mLevel) { +            case DEBUG: +                logText += "[DEBUG]"; +                break; +            case INFO: +                logText += "[INFO]"; +                break; +            case WARN: +                logText += "[WARN]"; +                break; +            case ERROR: +                logText += "[ERROR]"; +                break; +            case START: +                logText += "[START]"; +                break; +            case OK: +                logText += "[OK]"; +                break; +            case CANCELLED: +                logText += "[CANCELLED]"; +                break; +        } + +        // special case: first parameter may be a quantity +        if (entryParcel.mParameters != null && entryParcel.mParameters.length > 0 +                && entryParcel.mParameters[0] instanceof Integer) { +            logText += getResources().getQuantityString(entryParcel.mType.getMsgId(), +                    (Integer) entryParcel.mParameters[0], +                    entryParcel.mParameters); +        } else { +            logText += getResources().getString(entryParcel.mType.getMsgId(), +                    entryParcel.mParameters); +        } + +        if (entryParcel instanceof SubLogEntryParcel) { +            OperationResult subResult = ((SubLogEntryParcel) entryParcel).getSubResult(); +            LogEntryParcel subEntry = subResult.getLog().getLast(); +            if (subEntry != null) { +                //the first line of log of subResult is same as entryParcel, so replace logText +                logText = getPrintableOperationLog(subResult.getLog(), padding); +            } +        } + +        return logText; +    } + +    private void showExportLogDialog(final File exportFile) { + +        String title = this.getString(R.string.title_export_log); + +        String message = this.getString(R.string.specify_file_to_export_log_to); + +        FileHelper.saveFile(new FileHelper.FileDialogCallback() { +            @Override +            public void onFileSelected(File file, boolean checked) { +                writeToLogFile(mResult.getLog(), file); +            } +        }, this.getActivity().getSupportFragmentManager(), title, message, exportFile, null); +    } + +    private static class LogExportResult extends OperationResult { + +        public static Creator<LogExportResult> CREATOR = new Creator<LogExportResult>() { +            public LogExportResult createFromParcel(final Parcel source) { +                return new LogExportResult(source); +            } + +            public LogExportResult[] newArray(final int size) { +                return new LogExportResult[size]; +            } +        }; + +        public LogExportResult(int result, OperationLog log) { +            super(result, log); +        } + +        /** +         * trivial but necessary to implement the Parcelable protocol. +         */ +        public LogExportResult(Parcel source) { +            super(source); +        }      }      @Override @@ -109,7 +304,7 @@ public class LogDisplayFragment extends ListFragment implements OnItemClickListe                  mSecondImg = secondImg;              }          } - +        // Check if convertView.setPadding is redundant          @Override          public View getView(int position, View convertView, ViewGroup parent) {              LogEntryParcel entry = getItem(position); @@ -132,7 +327,6 @@ public class LogDisplayFragment extends ListFragment implements OnItemClickListe              if (entry instanceof SubLogEntryParcel) {                  ih.mSub.setVisibility(View.VISIBLE);                  convertView.setClickable(false); -                  convertView.setPadding((entry.mIndent) * dipFactor, 0, 0, 0);                  OperationResult result = ((SubLogEntryParcel) entry).getSubResult(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java index d5ca08936..bb669f6b8 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java @@ -46,6 +46,7 @@ import org.sufficientlysecure.keychain.R;  import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;  import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey;  import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing; +import org.sufficientlysecure.keychain.pgp.KeyRing;  import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;  import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;  import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing; @@ -149,6 +150,7 @@ public class PassphraseDialogActivity extends FragmentActivity {      public static class PassphraseDialogFragment extends DialogFragment implements TextView.OnEditorActionListener {          private EditText mPassphraseEditText; +        private TextView mPassphraseText;          private View mInput, mProgress;          private CanonicalizedSecretKeyRing mSecretRing = null; @@ -167,7 +169,7 @@ public class PassphraseDialogActivity extends FragmentActivity {              // if the dialog is displayed from the application class, design is missing              // hack to get holo design (which is not automatically applied due to activity's Theme.NoDisplay              ContextThemeWrapper theme = new ContextThemeWrapper(activity, -                    R.style.Theme_AppCompat_Light); +                    R.style.Theme_AppCompat_Light_Dialog);              mSubKeyId = getArguments().getLong(EXTRA_SUBKEY_ID);              mServiceIntent = getArguments().getParcelable(EXTRA_DATA); @@ -176,13 +178,30 @@ public class PassphraseDialogActivity extends FragmentActivity {              alert.setTitle(R.string.title_unlock); +            LayoutInflater inflater = LayoutInflater.from(theme); +            View view = inflater.inflate(R.layout.passphrase_dialog, null); +            alert.setView(view); + +            mPassphraseText = (TextView) view.findViewById(R.id.passphrase_text); +            mPassphraseEditText = (EditText) view.findViewById(R.id.passphrase_passphrase); +            mInput = view.findViewById(R.id.input); +            mProgress = view.findViewById(R.id.progress); + +            alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { + +                @Override +                public void onClick(DialogInterface dialog, int id) { +                    dialog.cancel(); +                } +            }); +              String userId;              CanonicalizedSecretKey.SecretKeyType keyType = CanonicalizedSecretKey.SecretKeyType.PASSPHRASE; +            String message;              if (mSubKeyId == Constants.key.symmetric || mSubKeyId == Constants.key.none) { -                alert.setMessage(R.string.passphrase_for_symmetric_encryption); +                message = getString(R.string.passphrase_for_symmetric_encryption);              } else { -                String message;                  try {                      ProviderHelper helper = new ProviderHelper(activity);                      mSecretRing = helper.getCanonicalizedSecretKeyRing( @@ -191,7 +210,13 @@ public class PassphraseDialogActivity extends FragmentActivity {                      // above can't be statically verified to have been set in all cases because                      // the catch clause doesn't return.                      try { -                        userId = mSecretRing.getPrimaryUserIdWithFallback(); +                        String mainUserId = mSecretRing.getPrimaryUserIdWithFallback(); +                        String[] mainUserIdSplit = KeyRing.splitUserId(mainUserId); +                        if (mainUserIdSplit[0] != null) { +                            userId = mainUserIdSplit[0]; +                        } else { +                            userId = getString(R.string.user_id_no_name); +                        }                      } catch (PgpKeyNotFoundException e) {                          userId = null;                      } @@ -228,33 +253,16 @@ public class PassphraseDialogActivity extends FragmentActivity {                      alert.setCancelable(false);                      return alert.create();                  } - -                alert.setMessage(message);              } -            LayoutInflater inflater = LayoutInflater.from(theme); -            View view = inflater.inflate(R.layout.passphrase_dialog, null); -            alert.setView(view); - -            mPassphraseEditText = (EditText) view.findViewById(R.id.passphrase_passphrase); -            mInput = view.findViewById(R.id.input); -            mProgress = view.findViewById(R.id.progress); - -            alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { - -                @Override -                public void onClick(DialogInterface dialog, int id) { -                    dialog.cancel(); -                } -            }); - +            mPassphraseText.setText(message);              if (keyType == CanonicalizedSecretKey.SecretKeyType.PATTERN) {                  // start pattern dialog and show progress circle here...  //                Intent patternActivity = new Intent(getActivity(), LockPatternActivity.class);  //                patternActivity.putExtra(LockPatternActivity.EXTRA_PATTERN, "123");  //                startActivityForResult(patternActivity, REQUEST_CODE_ENTER_PATTERN); -                mInput.setVisibility(View.GONE); +                mInput.setVisibility(View.INVISIBLE);                  mProgress.setVisibility(View.VISIBLE);              } else {                  // Hack to open keyboard. @@ -322,7 +330,7 @@ public class PassphraseDialogActivity extends FragmentActivity {                          return;                      } -                    mInput.setVisibility(View.GONE); +                    mInput.setVisibility(View.INVISIBLE);                      mProgress.setVisibility(View.VISIBLE);                      positive.setEnabled(false); @@ -364,7 +372,7 @@ public class PassphraseDialogActivity extends FragmentActivity {                                  mPassphraseEditText.setText("");                                  mPassphraseEditText.setError(getString(R.string.wrong_passphrase));                                  mInput.setVisibility(View.VISIBLE); -                                mProgress.setVisibility(View.GONE); +                                mProgress.setVisibility(View.INVISIBLE);                                  positive.setEnabled(true);                                  return;                              } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyFragment.java index afec3bf06..65d7eca37 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyFragment.java @@ -46,6 +46,7 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;  import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;  import org.sufficientlysecure.keychain.ui.adapter.SelectKeyCursorAdapter;  import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State;  import java.util.Vector; @@ -136,7 +137,7 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements T          mSearchView.setId(SEARCH_ID);          mSearchView.setHint(R.string.menu_search);          mSearchView.setCompoundDrawablesWithIntrinsicBounds( -                getResources().getDrawable(R.drawable.ic_action_search), null, null, null); +                getResources().getDrawable(R.drawable.ic_search_grey_24dp), null, null, null);          linearLayout.addView(mSearchView, new FrameLayout.LayoutParams(                  ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); @@ -376,15 +377,15 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements T                  // Check if key is viable for our purposes                  if (cursor.getInt(mIndexHasEncrypt) == 0) {                      h.statusIcon.setVisibility(View.VISIBLE); -                    KeyFormattingUtils.setStatusImage(mContext, h.statusIcon, KeyFormattingUtils.STATE_UNAVAILABLE); +                    KeyFormattingUtils.setStatusImage(mContext, h.statusIcon, State.UNAVAILABLE);                      enabled = false;                  } else if (cursor.getInt(mIndexIsVerified) != 0) {                      h.statusIcon.setVisibility(View.VISIBLE); -                    KeyFormattingUtils.setStatusImage(mContext, h.statusIcon, KeyFormattingUtils.STATE_VERIFIED); +                    KeyFormattingUtils.setStatusImage(mContext, h.statusIcon, State.VERIFIED);                      enabled = true;                  } else {                      h.statusIcon.setVisibility(View.VISIBLE); -                    KeyFormattingUtils.setStatusImage(mContext, h.statusIcon, KeyFormattingUtils.STATE_UNVERIFIED); +                    KeyFormattingUtils.setStatusImage(mContext, h.statusIcon, State.UNVERIFIED);                      enabled = true;                  }              } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java index 53986a392..210960b65 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java @@ -33,8 +33,6 @@ import android.view.ViewGroup;  import android.widget.LinearLayout;  import org.spongycastle.bcpg.CompressionAlgorithmTags; -import org.spongycastle.bcpg.HashAlgorithmTags; -import org.spongycastle.openpgp.PGPEncryptedData;  import org.sufficientlysecure.keychain.Constants;  import org.sufficientlysecure.keychain.R;  import org.sufficientlysecure.keychain.ui.widget.IntegerListPreference; @@ -93,12 +91,6 @@ public class SettingsActivity extends PreferenceActivity {              initializePassphraseCacheTtl(                      (IntegerListPreference) findPreference(Constants.Pref.PASSPHRASE_CACHE_TTL)); -            initializeEncryptionAlgorithm( -                    (IntegerListPreference) findPreference(Constants.Pref.DEFAULT_ENCRYPTION_ALGORITHM)); - -            initializeHashAlgorithm( -                    (IntegerListPreference) findPreference(Constants.Pref.DEFAULT_HASH_ALGORITHM)); -              int[] valueIds = new int[]{                      CompressionAlgorithmTags.UNCOMPRESSED,                      CompressionAlgorithmTags.ZIP, @@ -115,20 +107,6 @@ public class SettingsActivity extends PreferenceActivity {                  values[i] = "" + valueIds[i];              } -            initializeMessageCompression( -                    (IntegerListPreference) findPreference(Constants.Pref.DEFAULT_MESSAGE_COMPRESSION), -                    entries, values); - -            initializeFileCompression( -                    (IntegerListPreference) findPreference(Constants.Pref.DEFAULT_FILE_COMPRESSION), -                    entries, values); - -            initializeAsciiArmor( -                    (CheckBoxPreference) findPreference(Constants.Pref.DEFAULT_ASCII_ARMOR)); - -            initializeWriteVersionHeader( -                    (CheckBoxPreference) findPreference(Constants.Pref.WRITE_VERSION_HEADER)); -              initializeUseDefaultYubikeyPin(                      (CheckBoxPreference) findPreference(Constants.Pref.USE_DEFAULT_YUBIKEY_PIN)); @@ -265,12 +243,6 @@ public class SettingsActivity extends PreferenceActivity {              initializePassphraseCacheTtl(                      (IntegerListPreference) findPreference(Constants.Pref.PASSPHRASE_CACHE_TTL)); -            initializeEncryptionAlgorithm( -                    (IntegerListPreference) findPreference(Constants.Pref.DEFAULT_ENCRYPTION_ALGORITHM)); - -            initializeHashAlgorithm( -                    (IntegerListPreference) findPreference(Constants.Pref.DEFAULT_HASH_ALGORITHM)); -              int[] valueIds = new int[]{                      CompressionAlgorithmTags.UNCOMPRESSED,                      CompressionAlgorithmTags.ZIP, @@ -290,20 +262,6 @@ public class SettingsActivity extends PreferenceActivity {                  values[i] = "" + valueIds[i];              } -            initializeMessageCompression( -                    (IntegerListPreference) findPreference(Constants.Pref.DEFAULT_MESSAGE_COMPRESSION), -                    entries, values); - -            initializeFileCompression( -                    (IntegerListPreference) findPreference(Constants.Pref.DEFAULT_FILE_COMPRESSION), -                    entries, values); - -            initializeAsciiArmor( -                    (CheckBoxPreference) findPreference(Constants.Pref.DEFAULT_ASCII_ARMOR)); - -            initializeWriteVersionHeader( -                    (CheckBoxPreference) findPreference(Constants.Pref.WRITE_VERSION_HEADER)); -              initializeUseDefaultYubikeyPin(                      (CheckBoxPreference) findPreference(Constants.Pref.USE_DEFAULT_YUBIKEY_PIN)); @@ -344,113 +302,6 @@ public class SettingsActivity extends PreferenceActivity {                  });      } -    private static void initializeEncryptionAlgorithm(final IntegerListPreference mEncryptionAlgorithm) { -        int valueIds[] = {PGPEncryptedData.AES_128, PGPEncryptedData.AES_192, -                PGPEncryptedData.AES_256, PGPEncryptedData.BLOWFISH, PGPEncryptedData.TWOFISH, -                PGPEncryptedData.CAST5, PGPEncryptedData.DES, PGPEncryptedData.TRIPLE_DES, -                PGPEncryptedData.IDEA,}; -        String entries[] = {"AES-128", "AES-192", "AES-256", "Blowfish", "Twofish", "CAST5", -                "DES", "Triple DES", "IDEA",}; -        String values[] = new String[valueIds.length]; -        for (int i = 0; i < values.length; ++i) { -            values[i] = "" + valueIds[i]; -        } -        mEncryptionAlgorithm.setEntries(entries); -        mEncryptionAlgorithm.setEntryValues(values); -        mEncryptionAlgorithm.setValue("" + sPreferences.getDefaultEncryptionAlgorithm()); -        mEncryptionAlgorithm.setSummary(mEncryptionAlgorithm.getEntry()); -        mEncryptionAlgorithm -                .setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { -                    public boolean onPreferenceChange(Preference preference, Object newValue) { -                        mEncryptionAlgorithm.setValue(newValue.toString()); -                        mEncryptionAlgorithm.setSummary(mEncryptionAlgorithm.getEntry()); -                        sPreferences.setDefaultEncryptionAlgorithm(Integer.parseInt(newValue -                                .toString())); -                        return false; -                    } -                }); -    } - -    private static void initializeHashAlgorithm(final IntegerListPreference mHashAlgorithm) { -        int[] valueIds = new int[]{HashAlgorithmTags.RIPEMD160, -                HashAlgorithmTags.SHA1, HashAlgorithmTags.SHA224, HashAlgorithmTags.SHA256, -                HashAlgorithmTags.SHA384, HashAlgorithmTags.SHA512,}; -        String[] entries = new String[]{"RIPEMD-160", "SHA-1", "SHA-224", "SHA-256", "SHA-384", -                "SHA-512",}; -        String[] values = new String[valueIds.length]; -        for (int i = 0; i < values.length; ++i) { -            values[i] = "" + valueIds[i]; -        } -        mHashAlgorithm.setEntries(entries); -        mHashAlgorithm.setEntryValues(values); -        mHashAlgorithm.setValue("" + sPreferences.getDefaultHashAlgorithm()); -        mHashAlgorithm.setSummary(mHashAlgorithm.getEntry()); -        mHashAlgorithm.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { -            public boolean onPreferenceChange(Preference preference, Object newValue) { -                mHashAlgorithm.setValue(newValue.toString()); -                mHashAlgorithm.setSummary(mHashAlgorithm.getEntry()); -                sPreferences.setDefaultHashAlgorithm(Integer.parseInt(newValue.toString())); -                return false; -            } -        }); -    } - -    private static void initializeMessageCompression(final IntegerListPreference mMessageCompression, -                                                     String[] entries, String[] values) { -        mMessageCompression.setEntries(entries); -        mMessageCompression.setEntryValues(values); -        mMessageCompression.setValue("" + sPreferences.getDefaultMessageCompression()); -        mMessageCompression.setSummary(mMessageCompression.getEntry()); -        mMessageCompression -                .setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { -                    public boolean onPreferenceChange(Preference preference, Object newValue) { -                        mMessageCompression.setValue(newValue.toString()); -                        mMessageCompression.setSummary(mMessageCompression.getEntry()); -                        sPreferences.setDefaultMessageCompression(Integer.parseInt(newValue -                                .toString())); -                        return false; -                    } -                }); -    } - -    private static void initializeFileCompression -            (final IntegerListPreference mFileCompression, String[] entries, String[] values) { -        mFileCompression.setEntries(entries); -        mFileCompression.setEntryValues(values); -        mFileCompression.setValue("" + sPreferences.getDefaultFileCompression()); -        mFileCompression.setSummary(mFileCompression.getEntry()); -        mFileCompression.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { -            public boolean onPreferenceChange(Preference preference, Object newValue) { -                mFileCompression.setValue(newValue.toString()); -                mFileCompression.setSummary(mFileCompression.getEntry()); -                sPreferences.setDefaultFileCompression(Integer.parseInt(newValue.toString())); -                return false; -            } -        }); -    } - -    private static void initializeAsciiArmor(final CheckBoxPreference mAsciiArmor) { -        mAsciiArmor.setChecked(sPreferences.getDefaultAsciiArmor()); -        mAsciiArmor.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { -            public boolean onPreferenceChange(Preference preference, Object newValue) { -                mAsciiArmor.setChecked((Boolean) newValue); -                sPreferences.setDefaultAsciiArmor((Boolean) newValue); -                return false; -            } -        }); -    } - -    private static void initializeWriteVersionHeader(final CheckBoxPreference mWriteVersionHeader) { -        mWriteVersionHeader.setChecked(sPreferences.getWriteVersionHeader()); -        mWriteVersionHeader.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { -            public boolean onPreferenceChange(Preference preference, Object newValue) { -                mWriteVersionHeader.setChecked((Boolean) newValue); -                sPreferences.setWriteVersionHeader((Boolean) newValue); -                return false; -            } -        }); -    } -      private static void initializeSearchKeyserver(final CheckBoxPreference mSearchKeyserver) {          Preferences.CloudSearchPrefs prefs = sPreferences.getCloudSearchPrefs();          mSearchKeyserver.setChecked(prefs.searchKeyserver); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java index 0be6c26f6..1b4fc503c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -74,16 +74,15 @@ import org.sufficientlysecure.keychain.service.KeychainIntentService;  import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;  import org.sufficientlysecure.keychain.ui.util.FormattingUtils;  import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State;  import org.sufficientlysecure.keychain.ui.util.Notify;  import org.sufficientlysecure.keychain.ui.util.QrCodeUtils; -import org.sufficientlysecure.keychain.ui.widget.AspectRatioImageView;  import org.sufficientlysecure.keychain.util.ContactHelper;  import org.sufficientlysecure.keychain.util.ExportHelper;  import org.sufficientlysecure.keychain.util.Log;  import org.sufficientlysecure.keychain.util.Preferences;  import java.util.ArrayList; -import java.util.Date;  import java.util.HashMap;  public class ViewKeyActivity extends BaseActivity implements @@ -105,7 +104,7 @@ public class ViewKeyActivity extends BaseActivity implements      private ImageButton mActionEncryptText;      private ImageButton mActionNfc;      private FloatingActionButton mFab; -    private AspectRatioImageView mPhoto; +    private ImageView mPhoto;      private ImageView mQrCode;      private CardView mQrCodeLayout; @@ -121,6 +120,9 @@ public class ViewKeyActivity extends BaseActivity implements      private boolean mIsSecret = false;      private boolean mHasEncrypt = false;      private boolean mIsVerified = false; +    private boolean mIsRevoked = false; +    private boolean mIsExpired = false; +      private MenuItem mRefreshItem;      private boolean mIsRefreshing;      private Animation mRotate, mRotateSpin; @@ -147,7 +149,7 @@ public class ViewKeyActivity extends BaseActivity implements          mActionEncryptText = (ImageButton) findViewById(R.id.view_key_action_encrypt_text);          mActionNfc = (ImageButton) findViewById(R.id.view_key_action_nfc);          mFab = (FloatingActionButton) findViewById(R.id.fab); -        mPhoto = (AspectRatioImageView) findViewById(R.id.view_key_photo); +        mPhoto = (ImageView) findViewById(R.id.view_key_photo);          mQrCode = (ImageView) findViewById(R.id.view_key_qr_code);          mQrCodeLayout = (CardView) findViewById(R.id.view_key_qr_code_layout); @@ -173,7 +175,7 @@ public class ViewKeyActivity extends BaseActivity implements              }          }); -        mRotate =  AnimationUtils.loadAnimation(this, R.anim.rotate); +        mRotate = AnimationUtils.loadAnimation(this, R.anim.rotate);          mRotate.setRepeatCount(Animation.INFINITE);          mRotate.setAnimationListener(new Animation.AnimationListener() {              @Override @@ -347,7 +349,7 @@ public class ViewKeyActivity extends BaseActivity implements          MenuItem editKey = menu.findItem(R.id.menu_key_view_edit);          editKey.setVisible(mIsSecret);          MenuItem certifyFingerprint = menu.findItem(R.id.menu_key_view_certify_fingerprint); -        certifyFingerprint.setVisible(!mIsSecret && !mIsVerified); +        certifyFingerprint.setVisible(!mIsSecret && !mIsVerified && !mIsExpired && !mIsRevoked);          return true;      } @@ -398,12 +400,12 @@ public class ViewKeyActivity extends BaseActivity implements      private void certifyImmediate() {          Intent intent = new Intent(this, CertifyKeyActivity.class); -        intent.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS, new long[]{ mMasterKeyId }); +        intent.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS, new long[]{mMasterKeyId});          startCertifyIntent(intent);      } -    private void startCertifyIntent (Intent intent) { +    private void startCertifyIntent(Intent intent) {          // Message is received after signing is done in KeychainIntentService          KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this) {              public void handleMessage(Message message) { @@ -750,7 +752,7 @@ public class ViewKeyActivity extends BaseActivity implements              KeychainContract.KeyRings.MASTER_KEY_ID,              KeychainContract.KeyRings.USER_ID,              KeychainContract.KeyRings.IS_REVOKED, -            KeychainContract.KeyRings.EXPIRY, +            KeychainContract.KeyRings.IS_EXPIRED,              KeychainContract.KeyRings.VERIFIED,              KeychainContract.KeyRings.HAS_ANY_SECRET,              KeychainContract.KeyRings.FINGERPRINT, @@ -760,7 +762,7 @@ public class ViewKeyActivity extends BaseActivity implements      static final int INDEX_MASTER_KEY_ID = 1;      static final int INDEX_USER_ID = 2;      static final int INDEX_IS_REVOKED = 3; -    static final int INDEX_EXPIRY = 4; +    static final int INDEX_IS_EXPIRED = 4;      static final int INDEX_VERIFIED = 5;      static final int INDEX_HAS_ANY_SECRET = 6;      static final int INDEX_FINGERPRINT = 7; @@ -809,9 +811,8 @@ public class ViewKeyActivity extends BaseActivity implements                      mIsSecret = data.getInt(INDEX_HAS_ANY_SECRET) != 0;                      mHasEncrypt = data.getInt(INDEX_HAS_ENCRYPT) != 0; -                    boolean isRevoked = data.getInt(INDEX_IS_REVOKED) > 0; -                    boolean isExpired = !data.isNull(INDEX_EXPIRY) -                            && new Date(data.getLong(INDEX_EXPIRY) * 1000).before(new Date()); +                    mIsRevoked = data.getInt(INDEX_IS_REVOKED) > 0; +                    mIsExpired = data.getInt(INDEX_IS_EXPIRED) != 0;                      mIsVerified = data.getInt(INDEX_VERIFIED) > 0;                      // if the refresh animation isn't playing @@ -821,10 +822,10 @@ public class ViewKeyActivity extends BaseActivity implements                          // this is done at the end of the animation otherwise                      } -                    AsyncTask<String, Void, Bitmap> photoTask = -                            new AsyncTask<String, Void, Bitmap>() { -                                protected Bitmap doInBackground(String... fingerprint) { -                                    return ContactHelper.photoFromFingerprint(getContentResolver(), fingerprint[0]); +                    AsyncTask<Long, Void, Bitmap> photoTask = +                            new AsyncTask<Long, Void, Bitmap>() { +                                protected Bitmap doInBackground(Long... mMasterKeyId) { +                                    return ContactHelper.loadPhotoByMasterKeyId(getContentResolver(), mMasterKeyId[0], true);                                  }                                  protected void onPostExecute(Bitmap photo) { @@ -835,11 +836,11 @@ public class ViewKeyActivity extends BaseActivity implements                      // Note: order is important                      int color; -                    if (isRevoked) { +                    if (mIsRevoked) {                          mStatusText.setText(R.string.view_key_revoked);                          mStatusImage.setVisibility(View.VISIBLE);                          KeyFormattingUtils.setStatusImage(this, mStatusImage, mStatusText, -                                KeyFormattingUtils.STATE_REVOKED, R.color.icons, true); +                                State.REVOKED, R.color.icons, true);                          color = getResources().getColor(R.color.android_red_light);                          mActionEncryptFile.setVisibility(View.GONE); @@ -847,7 +848,7 @@ public class ViewKeyActivity extends BaseActivity implements                          mActionNfc.setVisibility(View.GONE);                          mFab.setVisibility(View.GONE);                          mQrCodeLayout.setVisibility(View.GONE); -                    } else if (isExpired) { +                    } else if (mIsExpired) {                          if (mIsSecret) {                              mStatusText.setText(R.string.view_key_expired_secret);                          } else { @@ -855,7 +856,7 @@ public class ViewKeyActivity extends BaseActivity implements                          }                          mStatusImage.setVisibility(View.VISIBLE);                          KeyFormattingUtils.setStatusImage(this, mStatusImage, mStatusText, -                                KeyFormattingUtils.STATE_EXPIRED, R.color.icons, true); +                                State.EXPIRED, R.color.icons, true);                          color = getResources().getColor(R.color.android_red_light);                          mActionEncryptFile.setVisibility(View.GONE); @@ -868,10 +869,10 @@ public class ViewKeyActivity extends BaseActivity implements                          mStatusImage.setVisibility(View.GONE);                          color = getResources().getColor(R.color.primary);                          // reload qr code only if the fingerprint changed -                        if ( !mFingerprint.equals(oldFingerprint)) { +                        if (!mFingerprint.equals(oldFingerprint)) {                              loadQrCode(mFingerprint);                          } -                        photoTask.execute(mFingerprint); +                        photoTask.execute(mMasterKeyId);                          mQrCodeLayout.setVisibility(View.VISIBLE);                          // and place leftOf qr code @@ -915,16 +916,16 @@ public class ViewKeyActivity extends BaseActivity implements                              mStatusText.setText(R.string.view_key_verified);                              mStatusImage.setVisibility(View.VISIBLE);                              KeyFormattingUtils.setStatusImage(this, mStatusImage, mStatusText, -                                    KeyFormattingUtils.STATE_VERIFIED, R.color.icons, true); +                                    State.VERIFIED, R.color.icons, true);                              color = getResources().getColor(R.color.primary); -                            photoTask.execute(mFingerprint); +                            photoTask.execute(mMasterKeyId);                              mFab.setVisibility(View.GONE);                          } else {                              mStatusText.setText(R.string.view_key_unverified);                              mStatusImage.setVisibility(View.VISIBLE);                              KeyFormattingUtils.setStatusImage(this, mStatusImage, mStatusText, -                                    KeyFormattingUtils.STATE_UNVERIFIED, R.color.icons, true); +                                    State.UNVERIFIED, R.color.icons, true);                              color = getResources().getColor(R.color.android_orange_light);                              mFab.setVisibility(View.VISIBLE); @@ -932,27 +933,21 @@ public class ViewKeyActivity extends BaseActivity implements                      }                      if (mPreviousColor == 0 || mPreviousColor == color) { -                        mToolbar.setBackgroundColor(color);                          mStatusBar.setBackgroundColor(color);                          mBigToolbar.setBackgroundColor(color);                          mPreviousColor = color;                      } else {                          ObjectAnimator colorFade1 = -                                ObjectAnimator.ofObject(mToolbar, "backgroundColor", -                                        new ArgbEvaluator(), mPreviousColor, color); -                        ObjectAnimator colorFade2 =                                  ObjectAnimator.ofObject(mStatusBar, "backgroundColor",                                          new ArgbEvaluator(), mPreviousColor, color); -                        ObjectAnimator colorFade3 = +                        ObjectAnimator colorFade2 =                                  ObjectAnimator.ofObject(mBigToolbar, "backgroundColor",                                          new ArgbEvaluator(), mPreviousColor, color);                          colorFade1.setDuration(1200);                          colorFade2.setDuration(1200); -                        colorFade3.setDuration(1200);                          colorFade1.start();                          colorFade2.start(); -                        colorFade3.start();                          mPreviousColor = color;                      } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvActivity.java index 9d79b377c..9390e8a69 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvActivity.java @@ -43,8 +43,6 @@ import org.sufficientlysecure.keychain.util.ContactHelper;  import org.sufficientlysecure.keychain.util.ExportHelper;  import org.sufficientlysecure.keychain.util.Log; -import java.util.Date; -  public class ViewKeyAdvActivity extends BaseActivity implements          LoaderManager.LoaderCallbacks<Cursor> { @@ -159,7 +157,7 @@ public class ViewKeyAdvActivity extends BaseActivity implements              KeychainContract.KeyRings.MASTER_KEY_ID,              KeychainContract.KeyRings.USER_ID,              KeychainContract.KeyRings.IS_REVOKED, -            KeychainContract.KeyRings.EXPIRY, +            KeychainContract.KeyRings.IS_EXPIRED,              KeychainContract.KeyRings.VERIFIED,              KeychainContract.KeyRings.HAS_ANY_SECRET      }; @@ -167,7 +165,7 @@ public class ViewKeyAdvActivity extends BaseActivity implements      static final int INDEX_MASTER_KEY_ID = 1;      static final int INDEX_USER_ID = 2;      static final int INDEX_IS_REVOKED = 3; -    static final int INDEX_EXPIRY = 4; +    static final int INDEX_IS_EXPIRED = 4;      static final int INDEX_VERIFIED = 5;      static final int INDEX_HAS_ANY_SECRET = 6; @@ -212,8 +210,7 @@ public class ViewKeyAdvActivity extends BaseActivity implements                      boolean isSecret = data.getInt(INDEX_HAS_ANY_SECRET) != 0;                      boolean isRevoked = data.getInt(INDEX_IS_REVOKED) > 0; -                    boolean isExpired = !data.isNull(INDEX_EXPIRY) -                            && new Date(data.getLong(INDEX_EXPIRY) * 1000).before(new Date()); +                    boolean isExpired = data.getInt(INDEX_IS_EXPIRED) != 0;                      boolean isVerified = data.getInt(INDEX_VERIFIED) > 0;                      // Note: order is important diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareFragment.java index 8d0a2dd1d..95a6faea9 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareFragment.java @@ -260,7 +260,7 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements      static final String[] UNIFIED_PROJECTION = new String[]{              KeyRings._ID, KeyRings.MASTER_KEY_ID, KeyRings.HAS_ANY_SECRET,              KeyRings.USER_ID, KeyRings.FINGERPRINT, -            KeyRings.ALGORITHM, KeyRings.KEY_SIZE, KeyRings.CREATION, KeyRings.EXPIRY, +            KeyRings.ALGORITHM, KeyRings.KEY_SIZE, KeyRings.CREATION, KeyRings.IS_EXPIRED,      };      static final int INDEX_UNIFIED_MASTER_KEY_ID = 1; @@ -270,7 +270,7 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements      static final int INDEX_UNIFIED_ALGORITHM = 5;      static final int INDEX_UNIFIED_KEY_SIZE = 6;      static final int INDEX_UNIFIED_CREATION = 7; -    static final int INDEX_UNIFIED_EXPIRY = 8; +    static final int INDEX_UNIFIED_ID_EXPIRED = 8;      public Loader<Cursor> onCreateLoader(int id, Bundle args) {          setContentShown(false); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvUserIdsFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvUserIdsFragment.java index c4e6639a8..7bfebaf62 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvUserIdsFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvUserIdsFragment.java @@ -114,12 +114,12 @@ public class ViewKeyAdvUserIdsFragment extends LoaderFragment implements      static final String[] UNIFIED_PROJECTION = new String[]{              KeyRings._ID, KeyRings.MASTER_KEY_ID, -            KeyRings.HAS_ANY_SECRET, KeyRings.IS_REVOKED, KeyRings.EXPIRY, KeyRings.HAS_ENCRYPT +            KeyRings.HAS_ANY_SECRET, KeyRings.IS_REVOKED, KeyRings.IS_EXPIRED, KeyRings.HAS_ENCRYPT      };      static final int INDEX_UNIFIED_MASTER_KEY_ID = 1;      static final int INDEX_UNIFIED_HAS_ANY_SECRET = 2;      static final int INDEX_UNIFIED_IS_REVOKED = 3; -    static final int INDEX_UNIFIED_EXPIRY = 4; +    static final int INDEX_UNIFIED_IS_EXPIRED = 4;      static final int INDEX_UNIFIED_HAS_ENCRYPT = 5;      public Loader<Cursor> onCreateLoader(int id, Bundle args) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java index 32630b459..628970b27 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java @@ -121,7 +121,7 @@ public class ViewKeyFragment extends LoaderFragment implements              KeychainContract.KeyRings.MASTER_KEY_ID,              KeychainContract.KeyRings.USER_ID,              KeychainContract.KeyRings.IS_REVOKED, -            KeychainContract.KeyRings.EXPIRY, +            KeychainContract.KeyRings.IS_EXPIRED,              KeychainContract.KeyRings.VERIFIED,              KeychainContract.KeyRings.HAS_ANY_SECRET,              KeychainContract.KeyRings.FINGERPRINT, @@ -131,7 +131,7 @@ public class ViewKeyFragment extends LoaderFragment implements      static final int INDEX_MASTER_KEY_ID = 1;      static final int INDEX_USER_ID = 2;      static final int INDEX_IS_REVOKED = 3; -    static final int INDEX_EXPIRY = 4; +    static final int INDEX_IS_EXPIRED = 4;      static final int INDEX_VERIFIED = 5;      static final int INDEX_HAS_ANY_SECRET = 6;      static final int INDEX_FINGERPRINT = 7; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyTrustFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyTrustFragment.java index 25edc7a02..d22f01a48 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyTrustFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyTrustFragment.java @@ -115,12 +115,12 @@ public class ViewKeyTrustFragment extends LoaderFragment implements      }      static final String[] TRUST_PROJECTION = new String[]{ -            KeyRings._ID, KeyRings.FINGERPRINT, KeyRings.IS_REVOKED, KeyRings.EXPIRY, +            KeyRings._ID, KeyRings.FINGERPRINT, KeyRings.IS_REVOKED, KeyRings.IS_EXPIRED,              KeyRings.HAS_ANY_SECRET, KeyRings.VERIFIED      };      static final int INDEX_TRUST_FINGERPRINT = 1;      static final int INDEX_TRUST_IS_REVOKED = 2; -    static final int INDEX_TRUST_EXPIRY = 3; +    static final int INDEX_TRUST_IS_EXPIRED = 3;      static final int INDEX_UNIFIED_HAS_ANY_SECRET = 4;      static final int INDEX_VERIFIED = 5; @@ -169,8 +169,7 @@ public class ViewKeyTrustFragment extends LoaderFragment implements                  nothingSpecial = false;              } else { -                Date expiryDate = new Date(data.getLong(INDEX_TRUST_EXPIRY) * 1000); -                if (!data.isNull(INDEX_TRUST_EXPIRY) && expiryDate.before(new Date())) { +                if (data.getInt(INDEX_TRUST_IS_EXPIRED) != 0) {                      // if expired, don’t trust it!                      message.append(getString(R.string.key_trust_expired)). diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java index 6ba9e26ad..429feb075 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java @@ -37,6 +37,7 @@ import org.sufficientlysecure.keychain.pgp.KeyRing;  import org.sufficientlysecure.keychain.ui.util.FormattingUtils;  import org.sufficientlysecure.keychain.ui.util.Highlighter;  import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State;  import java.util.ArrayList;  import java.util.HashMap; @@ -175,9 +176,9 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> {          }          if (entry.isRevoked()) { -            KeyFormattingUtils.setStatusImage(getContext(), holder.status, null, KeyFormattingUtils.STATE_REVOKED, R.color.bg_gray); +            KeyFormattingUtils.setStatusImage(getContext(), holder.status, null, State.REVOKED, R.color.bg_gray);          } else if (entry.isExpired()) { -            KeyFormattingUtils.setStatusImage(getContext(), holder.status, null, KeyFormattingUtils.STATE_EXPIRED, R.color.bg_gray); +            KeyFormattingUtils.setStatusImage(getContext(), holder.status, null, State.EXPIRED, R.color.bg_gray);          }          if (entry.isRevoked() || entry.isExpired()) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java index a836b35df..226fda20b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java @@ -33,6 +33,7 @@ import org.sufficientlysecure.keychain.pgp.KeyRing;  import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;  import org.sufficientlysecure.keychain.ui.util.Highlighter;  import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State;  /** @@ -133,11 +134,11 @@ abstract public class SelectKeyCursorAdapter extends CursorAdapter {          boolean enabled;          if (cursor.getInt(mIndexIsRevoked) != 0) {              h.statusIcon.setVisibility(View.VISIBLE); -            KeyFormattingUtils.setStatusImage(mContext, h.statusIcon, null, KeyFormattingUtils.STATE_REVOKED, R.color.bg_gray); +            KeyFormattingUtils.setStatusImage(mContext, h.statusIcon, null, State.REVOKED, R.color.bg_gray);              enabled = false;          } else if (cursor.getInt(mIndexIsExpiry) != 0) {              h.statusIcon.setVisibility(View.VISIBLE); -            KeyFormattingUtils.setStatusImage(mContext, h.statusIcon, null, KeyFormattingUtils.STATE_EXPIRED, R.color.bg_gray); +            KeyFormattingUtils.setStatusImage(mContext, h.statusIcon, null, State.EXPIRED, R.color.bg_gray);              enabled = false;          } else {              h.statusIcon.setVisibility(View.GONE); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java index ff5fbb49a..096dea51f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java @@ -272,12 +272,12 @@ public class SubkeysAdapter extends CursorAdapter {                      PorterDuff.Mode.SRC_IN);              if (isRevoked) { -                vStatus.setImageResource(R.drawable.status_signature_revoked_cutout_24px); +                vStatus.setImageResource(R.drawable.status_signature_revoked_cutout_24dp);                  vStatus.setColorFilter(                          mContext.getResources().getColor(R.color.bg_gray),                          PorterDuff.Mode.SRC_IN);              } else if (isExpired) { -                vStatus.setImageResource(R.drawable.status_signature_expired_cutout_24px); +                vStatus.setImageResource(R.drawable.status_signature_expired_cutout_24dp);                  vStatus.setColorFilter(                          mContext.getResources().getColor(R.color.bg_gray),                          PorterDuff.Mode.SRC_IN); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java index 6a4f61f4b..3486f1516 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java @@ -36,6 +36,7 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;  import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;  import org.sufficientlysecure.keychain.service.SaveKeyringParcel;  import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State;  public class UserIdsAdapter extends UserAttributesAdapter {      protected LayoutInflater mInflater; @@ -127,7 +128,7 @@ public class UserIdsAdapter extends UserAttributesAdapter {          if (isRevoked) {              // set revocation icon (can this even be primary?) -            KeyFormattingUtils.setStatusImage(mContext, vVerified, null, KeyFormattingUtils.STATE_REVOKED, R.color.bg_gray); +            KeyFormattingUtils.setStatusImage(mContext, vVerified, null, State.REVOKED, R.color.bg_gray);              // disable revoked user ids              vName.setEnabled(false); @@ -149,13 +150,13 @@ public class UserIdsAdapter extends UserAttributesAdapter {              int isVerified = cursor.getInt(INDEX_VERIFIED);              switch (isVerified) {                  case Certs.VERIFIED_SECRET: -                    KeyFormattingUtils.setStatusImage(mContext, vVerified, null, KeyFormattingUtils.STATE_VERIFIED, KeyFormattingUtils.DEFAULT_COLOR); +                    KeyFormattingUtils.setStatusImage(mContext, vVerified, null, State.VERIFIED, KeyFormattingUtils.DEFAULT_COLOR);                      break;                  case Certs.VERIFIED_SELF: -                    KeyFormattingUtils.setStatusImage(mContext, vVerified, null, KeyFormattingUtils.STATE_UNVERIFIED, KeyFormattingUtils.DEFAULT_COLOR); +                    KeyFormattingUtils.setStatusImage(mContext, vVerified, null, State.UNVERIFIED, KeyFormattingUtils.DEFAULT_COLOR);                      break;                  default: -                    KeyFormattingUtils.setStatusImage(mContext, vVerified, null, KeyFormattingUtils.STATE_INVALID, KeyFormattingUtils.DEFAULT_COLOR); +                    KeyFormattingUtils.setStatusImage(mContext, vVerified, null, State.INVALID, KeyFormattingUtils.DEFAULT_COLOR);                      break;              }          } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddSubkeyDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddSubkeyDialogFragment.java index d5376cbdc..0b1d39fc1 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddSubkeyDialogFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddSubkeyDialogFragment.java @@ -137,12 +137,10 @@ public class AddSubkeyDialogFragment extends DialogFragment {              }          }); -        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) { -            // date picker works based on default time zone -            Calendar minDateCal = Calendar.getInstance(TimeZone.getDefault()); -            minDateCal.add(Calendar.DAY_OF_YEAR, 1); // at least one day after creation (today) -            mExpiryDatePicker.setMinDate(minDateCal.getTime().getTime()); -        } +        // date picker works based on default time zone +        Calendar minDateCal = Calendar.getInstance(TimeZone.getDefault()); +        minDateCal.add(Calendar.DAY_OF_YEAR, 1); // at least one day after creation (today) +        mExpiryDatePicker.setMinDate(minDateCal.getTime().getTime());          {              ArrayList<Choice<Algorithm>> choices = new ArrayList<>(); @@ -283,7 +281,7 @@ public class AddSubkeyDialogFragment extends DialogFragment {                      // For EC keys, add a curve                      if (algorithm == Algorithm.ECDH || algorithm == Algorithm.ECDSA) {                          curve = ((Choice<Curve>) mCurveSpinner.getSelectedItem()).getId(); -                    // Otherwise, get a keysize +                        // Otherwise, get a keysize                      } else {                          keySize = getProperKeyLength(algorithm, getSelectedKeyLength());                      } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CustomAlertDialogBuilder.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CustomAlertDialogBuilder.java index d405b1dda..794af5b15 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CustomAlertDialogBuilder.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CustomAlertDialogBuilder.java @@ -1,3 +1,20 @@ +/* + * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ +  package org.sufficientlysecure.keychain.ui.dialog;  import android.app.AlertDialog; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java index 879e3f6da..07462b4ff 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java @@ -62,12 +62,9 @@ public class DeleteFileDialogFragment extends DialogFragment {          CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(activity); - -        alert.setIcon(R.drawable.ic_dialog_alert_holo_light); -        alert.setTitle(R.string.warning);          alert.setMessage(this.getString(R.string.file_delete_confirmation, deleteFilename)); -        alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { +        alert.setPositiveButton(R.string.btn_delete, new DialogInterface.OnClickListener() {              @Override              public void onClick(DialogInterface dialog, int id) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java index 802f0c11b..32789d53b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java @@ -83,8 +83,6 @@ public class DeleteKeyDialogFragment extends DialogFragment {          mMainMessage = (TextView) mInflateView.findViewById(R.id.mainMessage); -        builder.setTitle(R.string.warning); -          final boolean hasSecret;          // If only a single key has been selected @@ -110,12 +108,14 @@ public class DeleteKeyDialogFragment extends DialogFragment {                  }                  hasSecret = ((Long) data.get(KeyRings.HAS_ANY_SECRET)) == 1; -                // Set message depending on which key it is. -                mMainMessage.setText(getString( -                        hasSecret ? R.string.secret_key_deletion_confirmation -                                : R.string.public_key_deletetion_confirmation, -                        name -                )); +                if (hasSecret) { +                    // show title only for secret key deletions, +                    // see http://www.google.com/design/spec/components/dialogs.html#dialogs-behavior +                    builder.setTitle(getString(R.string.title_delete_secret_key, name)); +                    mMainMessage.setText(getString(R.string.secret_key_deletion_confirmation, name)); +                } else { +                    mMainMessage.setText(getString(R.string.public_key_deletetion_confirmation, name)); +                }              } catch (ProviderHelper.NotFoundException e) {                  dismiss();                  return null; @@ -125,7 +125,6 @@ public class DeleteKeyDialogFragment extends DialogFragment {              hasSecret = false;          } -        builder.setIcon(R.drawable.ic_dialog_alert_holo_light);          builder.setPositiveButton(R.string.btn_delete, new DialogInterface.OnClickListener() {              @Override              public void onClick(DialogInterface dialog, int which) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/EditSubkeyExpiryDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/EditSubkeyExpiryDialogFragment.java index fc618c9eb..37e05a61d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/EditSubkeyExpiryDialogFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/EditSubkeyExpiryDialogFragment.java @@ -25,11 +25,14 @@ import android.os.Message;  import android.os.Messenger;  import android.os.RemoteException;  import android.support.v4.app.DialogFragment; +import android.text.format.DateFormat;  import android.view.LayoutInflater;  import android.view.View;  import android.widget.CheckBox;  import android.widget.CompoundButton;  import android.widget.DatePicker; +import android.widget.LinearLayout; +import android.widget.TextView;  import org.sufficientlysecure.keychain.Constants;  import org.sufficientlysecure.keychain.R; @@ -97,61 +100,64 @@ public class EditSubkeyExpiryDialogFragment extends DialogFragment {          final CheckBox noExpiry = (CheckBox) view.findViewById(R.id.edit_subkey_expiry_no_expiry);          final DatePicker datePicker = (DatePicker) view.findViewById(R.id.edit_subkey_expiry_date_picker); +        final TextView currentExpiry = (TextView) view.findViewById(R.id.edit_subkey_expiry_current_expiry); +        final LinearLayout expiryLayout = (LinearLayout) view.findViewById(R.id.edit_subkey_expiry_layout);          noExpiry.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {              @Override              public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {                  if (isChecked) { -                    datePicker.setVisibility(View.GONE); +                    expiryLayout.setVisibility(View.GONE);                  } else { -                    datePicker.setVisibility(View.VISIBLE); +                    expiryLayout.setVisibility(View.VISIBLE);                  }              }          }); -        // init date picker with default selected date          if (expiry == 0L) {              noExpiry.setChecked(true); -            datePicker.setVisibility(View.GONE); - -            Calendar todayCal = Calendar.getInstance(TimeZone.getDefault()); -            if (creationCal.after(todayCal)) { -                // Note: This is just for the rare cases where creation is _after_ today - -                // set it to creation date +1 day (don't set it to creationCal, it would break crash -                // datePicker.setMinDate() execution with IllegalArgumentException -                Calendar creationCalPlusOne = (Calendar) creationCal.clone(); -                creationCalPlusOne.add(Calendar.DAY_OF_YEAR, 1); -                datePicker.init( -                        creationCalPlusOne.get(Calendar.YEAR), -                        creationCalPlusOne.get(Calendar.MONTH), -                        creationCalPlusOne.get(Calendar.DAY_OF_MONTH), -                        null -                ); - -            } else { -                // normally, just init with today -                datePicker.init( -                        todayCal.get(Calendar.YEAR), -                        todayCal.get(Calendar.MONTH), -                        todayCal.get(Calendar.DAY_OF_MONTH), -                        null -                ); -            } +            expiryLayout.setVisibility(View.GONE); + +            currentExpiry.setText(R.string.btn_no_date);          } else {              noExpiry.setChecked(false); -            datePicker.setVisibility(View.VISIBLE); +            expiryLayout.setVisibility(View.VISIBLE); + +            // convert from UTC to time zone of device +            Calendar expiryCalTimeZone = (Calendar) expiryCal.clone(); +            expiryCalTimeZone.setTimeZone(TimeZone.getDefault()); +            currentExpiry.setText(DateFormat.getDateFormat( +                    getActivity()).format(expiryCalTimeZone.getTime())); +        } + +        // date picker works based on default time zone +        Calendar todayCal = Calendar.getInstance(TimeZone.getDefault()); +        if (creationCal.after(todayCal)) { +            // NOTE: This is just for the rare cases where creation is _after_ today +            // Min Date: Creation date + 1 day -            // set date picker to current expiry +            Calendar creationCalPlusOne = (Calendar) creationCal.clone(); +            creationCalPlusOne.add(Calendar.DAY_OF_YEAR, 1); +            datePicker.setMinDate(creationCalPlusOne.getTime().getTime());              datePicker.init( -                    expiryCal.get(Calendar.YEAR), -                    expiryCal.get(Calendar.MONTH), -                    expiryCal.get(Calendar.DAY_OF_MONTH), +                    creationCalPlusOne.get(Calendar.YEAR), +                    creationCalPlusOne.get(Calendar.MONTH), +                    creationCalPlusOne.get(Calendar.DAY_OF_MONTH),                      null              ); -        } +        } else { +            // Min Date: today + 1 day -        datePicker.setMinDate(creationCal.getTime().getTime()); +            // at least one day after creation (today) +            todayCal.add(Calendar.DAY_OF_YEAR, 1); +            datePicker.setMinDate(todayCal.getTime().getTime()); +            datePicker.init( +                    todayCal.get(Calendar.YEAR), +                    todayCal.get(Calendar.MONTH), +                    todayCal.get(Calendar.DAY_OF_MONTH), +                    null +            ); +        }          alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {              @Override diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java index a05719072..a3ffe250b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java @@ -92,7 +92,6 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi          CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(activity);          alert.setTitle(title); -        alert.setMessage(R.string.enter_passphrase_twice);          LayoutInflater inflater = activity.getLayoutInflater();          View view = inflater.inflate(R.layout.passphrase_repeat_dialog, null); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java index 38ed88b9c..c5403e054 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java @@ -379,27 +379,29 @@ public class KeyFormattingUtils {      public static final int DEFAULT_COLOR = -1; -    public static final int STATE_REVOKED = 1; -    public static final int STATE_EXPIRED = 2; -    public static final int STATE_VERIFIED = 3; -    public static final int STATE_UNAVAILABLE = 4; -    public static final int STATE_ENCRYPTED = 5; -    public static final int STATE_NOT_ENCRYPTED = 6; -    public static final int STATE_UNVERIFIED = 7; -    public static final int STATE_UNKNOWN_KEY = 8; -    public static final int STATE_INVALID = 9; -    public static final int STATE_NOT_SIGNED = 10; - -    public static void setStatusImage(Context context, ImageView statusIcon, int state) { +    public static enum State { +        REVOKED, +        EXPIRED, +        VERIFIED, +        UNAVAILABLE, +        ENCRYPTED, +        NOT_ENCRYPTED, +        UNVERIFIED, +        UNKNOWN_KEY, +        INVALID, +        NOT_SIGNED +    } + +    public static void setStatusImage(Context context, ImageView statusIcon, State state) {          setStatusImage(context, statusIcon, null, state);      } -    public static void setStatusImage(Context context, ImageView statusIcon, TextView statusText, int state) { +    public static void setStatusImage(Context context, ImageView statusIcon, TextView statusText, State state) {          setStatusImage(context, statusIcon, statusText, state, KeyFormattingUtils.DEFAULT_COLOR, false);      }      public static void setStatusImage(Context context, ImageView statusIcon, TextView statusText, -                                      int state, int color) { +                                      State state, int color) {          setStatusImage(context, statusIcon, statusText, state, color, false);      } @@ -407,16 +409,16 @@ public class KeyFormattingUtils {       * Sets status image based on constant       */      public static void setStatusImage(Context context, ImageView statusIcon, TextView statusText, -                                      int state, int color, boolean big) { +                                      State state, int color, boolean big) {          switch (state) {              /** GREEN: everything is good **/ -            case STATE_VERIFIED: { +            case VERIFIED: {                  if (big) {                      statusIcon.setImageDrawable( -                            context.getResources().getDrawable(R.drawable.status_signature_verified_cutout_96px)); +                            context.getResources().getDrawable(R.drawable.status_signature_verified_cutout_96dp));                  } else {                      statusIcon.setImageDrawable( -                            context.getResources().getDrawable(R.drawable.status_signature_verified_cutout_24px)); +                            context.getResources().getDrawable(R.drawable.status_signature_verified_cutout_24dp));                  }                  if (color == KeyFormattingUtils.DEFAULT_COLOR) {                      color = R.color.android_green_light; @@ -428,9 +430,9 @@ public class KeyFormattingUtils {                  }                  break;              } -            case STATE_ENCRYPTED: { +            case ENCRYPTED: {                  statusIcon.setImageDrawable( -                        context.getResources().getDrawable(R.drawable.status_lock_closed_24px)); +                        context.getResources().getDrawable(R.drawable.status_lock_closed_24dp));                  if (color == KeyFormattingUtils.DEFAULT_COLOR) {                      color = R.color.android_green_light;                  } @@ -442,13 +444,13 @@ public class KeyFormattingUtils {                  break;              }              /** ORANGE: mostly bad... **/ -            case STATE_UNVERIFIED: { +            case UNVERIFIED: {                  if (big) {                      statusIcon.setImageDrawable( -                            context.getResources().getDrawable(R.drawable.status_signature_unverified_cutout_96px)); +                            context.getResources().getDrawable(R.drawable.status_signature_unverified_cutout_96dp));                  } else {                      statusIcon.setImageDrawable( -                            context.getResources().getDrawable(R.drawable.status_signature_unverified_cutout_24px)); +                            context.getResources().getDrawable(R.drawable.status_signature_unverified_cutout_24dp));                  }                  if (color == KeyFormattingUtils.DEFAULT_COLOR) {                      color = R.color.android_orange_light; @@ -460,9 +462,9 @@ public class KeyFormattingUtils {                  }                  break;              } -            case STATE_UNKNOWN_KEY: { +            case UNKNOWN_KEY: {                  statusIcon.setImageDrawable( -                        context.getResources().getDrawable(R.drawable.status_signature_unknown_cutout_24px)); +                        context.getResources().getDrawable(R.drawable.status_signature_unknown_cutout_24dp));                  if (color == KeyFormattingUtils.DEFAULT_COLOR) {                      color = R.color.android_orange_light;                  } @@ -474,13 +476,13 @@ public class KeyFormattingUtils {                  break;              }              /** RED: really bad... **/ -            case STATE_REVOKED: { +            case REVOKED: {                  if (big) {                      statusIcon.setImageDrawable( -                            context.getResources().getDrawable(R.drawable.status_signature_revoked_cutout_96px)); +                            context.getResources().getDrawable(R.drawable.status_signature_revoked_cutout_96dp));                  } else {                      statusIcon.setImageDrawable( -                            context.getResources().getDrawable(R.drawable.status_signature_revoked_cutout_24px)); +                            context.getResources().getDrawable(R.drawable.status_signature_revoked_cutout_24dp));                  }                  if (color == KeyFormattingUtils.DEFAULT_COLOR) {                      color = R.color.android_red_light; @@ -492,13 +494,13 @@ public class KeyFormattingUtils {                  }                  break;              } -            case STATE_EXPIRED: { +            case EXPIRED: {                  if (big) {                      statusIcon.setImageDrawable( -                            context.getResources().getDrawable(R.drawable.status_signature_expired_cutout_96px)); +                            context.getResources().getDrawable(R.drawable.status_signature_expired_cutout_96dp));                  } else {                      statusIcon.setImageDrawable( -                            context.getResources().getDrawable(R.drawable.status_signature_expired_cutout_24px)); +                            context.getResources().getDrawable(R.drawable.status_signature_expired_cutout_24dp));                  }                  if (color == KeyFormattingUtils.DEFAULT_COLOR) {                      color = R.color.android_red_light; @@ -510,9 +512,9 @@ public class KeyFormattingUtils {                  }                  break;              } -            case STATE_NOT_ENCRYPTED: { +            case NOT_ENCRYPTED: {                  statusIcon.setImageDrawable( -                        context.getResources().getDrawable(R.drawable.status_lock_open_24px)); +                        context.getResources().getDrawable(R.drawable.status_lock_open_24dp));                  if (color == KeyFormattingUtils.DEFAULT_COLOR) {                      color = R.color.android_red_light;                  } @@ -523,9 +525,9 @@ public class KeyFormattingUtils {                  }                  break;              } -            case STATE_NOT_SIGNED: { +            case NOT_SIGNED: {                  statusIcon.setImageDrawable( -                        context.getResources().getDrawable(R.drawable.status_signature_unknown_cutout_24px)); +                        context.getResources().getDrawable(R.drawable.status_signature_unknown_cutout_24dp));                  if (color == KeyFormattingUtils.DEFAULT_COLOR) {                      color = R.color.android_red_light;                  } @@ -536,9 +538,9 @@ public class KeyFormattingUtils {                  }                  break;              } -            case STATE_INVALID: { +            case INVALID: {                  statusIcon.setImageDrawable( -                        context.getResources().getDrawable(R.drawable.status_signature_invalid_cutout_24px)); +                        context.getResources().getDrawable(R.drawable.status_signature_invalid_cutout_24dp));                  if (color == KeyFormattingUtils.DEFAULT_COLOR) {                      color = R.color.android_red_light;                  } @@ -550,9 +552,9 @@ public class KeyFormattingUtils {                  break;              }              /** special **/ -            case STATE_UNAVAILABLE: { +            case UNAVAILABLE: {                  statusIcon.setImageDrawable( -                        context.getResources().getDrawable(R.drawable.status_signature_invalid_cutout_24px)); +                        context.getResources().getDrawable(R.drawable.status_signature_invalid_cutout_24dp));                  if (color == KeyFormattingUtils.DEFAULT_COLOR) {                      color = R.color.bg_gray;                  } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java index 6d0e6556f..fc912fccb 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java @@ -31,6 +31,7 @@ import org.sufficientlysecure.keychain.R;  import org.sufficientlysecure.keychain.provider.KeychainContract;  import org.sufficientlysecure.keychain.provider.KeychainDatabase;  import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State;  public class CertifyKeySpinner extends KeySpinner {      private long mHiddenMasterKeyId = Constants.key.none; @@ -103,16 +104,16 @@ public class CertifyKeySpinner extends KeySpinner {      @Override      boolean setStatus(Context context, Cursor cursor, ImageView statusView) {          if (cursor.getInt(mIndexIsRevoked) != 0) { -            KeyFormattingUtils.setStatusImage(getContext(), statusView, null, KeyFormattingUtils.STATE_REVOKED, R.color.bg_gray); +            KeyFormattingUtils.setStatusImage(getContext(), statusView, null, State.REVOKED, R.color.bg_gray);              return false;          }          if (cursor.getInt(mIndexIsExpired) != 0) { -            KeyFormattingUtils.setStatusImage(getContext(), statusView, null, KeyFormattingUtils.STATE_EXPIRED, R.color.bg_gray); +            KeyFormattingUtils.setStatusImage(getContext(), statusView, null, State.EXPIRED, R.color.bg_gray);              return false;          }          // don't invalidate the "None" entry, which is also null!          if (cursor.getPosition() != 0 && cursor.isNull(mIndexHasCertify)) { -            KeyFormattingUtils.setStatusImage(getContext(), statusView, null, KeyFormattingUtils.STATE_UNAVAILABLE, R.color.bg_gray); +            KeyFormattingUtils.setStatusImage(getContext(), statusView, null, State.UNAVAILABLE, R.color.bg_gray);              return false;          } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java index f05f5f96b..d20e2bc99 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java @@ -90,7 +90,7 @@ public class EncryptKeyCompletionView extends TokenCompleteTextView {      }      private void setImageByKey(ImageView view, EncryptionKey key) { -        Bitmap photo = ContactHelper.photoFromFingerprint(getContext().getContentResolver(), key.getFingerprint()); +        Bitmap photo = ContactHelper.getCachedPhotoByMasterKeyId(getContext().getContentResolver(), key.getKeyId());          if (photo != null) {              view.setImageBitmap(photo); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SignKeySpinner.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SignKeySpinner.java index fe91e306e..10327a6a4 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SignKeySpinner.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SignKeySpinner.java @@ -29,6 +29,7 @@ import android.widget.ImageView;  import org.sufficientlysecure.keychain.R;  import org.sufficientlysecure.keychain.provider.KeychainContract;  import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State;  public class SignKeySpinner extends KeySpinner {      public SignKeySpinner(Context context) { @@ -84,15 +85,15 @@ public class SignKeySpinner extends KeySpinner {      @Override      boolean setStatus(Context context, Cursor cursor, ImageView statusView) {          if (cursor.getInt(mIndexIsRevoked) != 0) { -            KeyFormattingUtils.setStatusImage(getContext(), statusView, null, KeyFormattingUtils.STATE_REVOKED, R.color.bg_gray); +            KeyFormattingUtils.setStatusImage(getContext(), statusView, null, State.REVOKED, R.color.bg_gray);              return false;          }          if (cursor.getInt(mIndexIsExpired) != 0) { -            KeyFormattingUtils.setStatusImage(getContext(), statusView, null, KeyFormattingUtils.STATE_EXPIRED, R.color.bg_gray); +            KeyFormattingUtils.setStatusImage(getContext(), statusView, null, State.EXPIRED, R.color.bg_gray);              return false;          }          if (cursor.getInt(mIndexHasSign) == 0) { -            KeyFormattingUtils.setStatusImage(getContext(), statusView, null, KeyFormattingUtils.STATE_UNAVAILABLE, R.color.bg_gray); +            KeyFormattingUtils.setStatusImage(getContext(), statusView, null, State.UNAVAILABLE, R.color.bg_gray);              return false;          } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ContactHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ContactHelper.java index 28480cee5..c66dc04d0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ContactHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ContactHelper.java @@ -20,18 +20,15 @@ package org.sufficientlysecure.keychain.util;  import android.accounts.Account;  import android.accounts.AccountManager;  import android.annotation.TargetApi; -import android.content.ContentProviderClient;  import android.content.ContentProviderOperation;  import android.content.ContentResolver;  import android.content.ContentUris; -import android.content.ContentValues;  import android.content.Context;  import android.database.Cursor;  import android.graphics.Bitmap;  import android.graphics.BitmapFactory;  import android.net.Uri;  import android.os.Build; -import android.os.RemoteException;  import android.provider.ContactsContract;  import android.util.Patterns; @@ -44,7 +41,6 @@ import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;  import java.io.InputStream;  import java.util.ArrayList; -import java.util.Date;  import java.util.HashMap;  import java.util.HashSet;  import java.util.List; @@ -53,40 +49,7 @@ import java.util.Set;  public class ContactHelper { -    public static final String[] KEYS_TO_CONTACT_PROJECTION = new String[]{ -            KeychainContract.KeyRings.USER_ID, -            KeychainContract.KeyRings.FINGERPRINT, -            KeychainContract.KeyRings.KEY_ID, -            KeychainContract.KeyRings.MASTER_KEY_ID, -            KeychainContract.KeyRings.EXPIRY, -            KeychainContract.KeyRings.IS_REVOKED}; - -    public static final int INDEX_USER_ID = 0; -    public static final int INDEX_FINGERPRINT = 1; -    public static final int INDEX_KEY_ID = 2; -    public static final int INDEX_MASTER_KEY_ID = 3; -    public static final int INDEX_EXPIRY = 4; -    public static final int INDEX_IS_REVOKED = 5; - -    public static final String[] USER_IDS_PROJECTION = new String[]{ -            UserPackets.USER_ID -    }; - -    public static final int INDEX_USER_IDS_USER_ID = 0; - -    public static final String NON_REVOKED_SELECTION = UserPackets.IS_REVOKED + "=0"; - -    public static final String[] ID_PROJECTION = new String[]{ContactsContract.RawContacts._ID}; -    public static final String[] SOURCE_ID_PROJECTION = new String[]{ContactsContract.RawContacts.SOURCE_ID}; - -    public static final String ACCOUNT_TYPE_AND_SOURCE_ID_SELECTION = -            ContactsContract.RawContacts.ACCOUNT_TYPE + "=? AND " + ContactsContract.RawContacts.SOURCE_ID + "=?"; -    public static final String ACCOUNT_TYPE_SELECTION = ContactsContract.RawContacts.ACCOUNT_TYPE + "=?"; -    public static final String RAW_CONTACT_AND_MIMETYPE_SELECTION = -            ContactsContract.Data.RAW_CONTACT_ID + "=? AND " + ContactsContract.Data.MIMETYPE + "=?"; -    public static final String ID_SELECTION = ContactsContract.RawContacts._ID + "=?"; - -    private static final Map<String, Bitmap> photoCache = new HashMap<>(); +    private static final Map<Long, Bitmap> photoCache = new HashMap<>();      public static List<String> getPossibleUserEmails(Context context) {          Set<String> accountMails = getAccountEmails(context); @@ -282,27 +245,30 @@ public class ContactHelper {          return null;      } -    public static Bitmap photoFromFingerprint(ContentResolver contentResolver, String fingerprint) { -        if (fingerprint == null) { +    public static Bitmap getCachedPhotoByMasterKeyId(ContentResolver contentResolver, long masterKeyId) { +        if (masterKeyId == -1) {              return null;          } -        if (!photoCache.containsKey(fingerprint)) { -            photoCache.put(fingerprint, loadPhotoFromFingerprint(contentResolver, fingerprint)); +        if (!photoCache.containsKey(masterKeyId)) { +            photoCache.put(masterKeyId, loadPhotoByMasterKeyId(contentResolver, masterKeyId, false));          } -        return photoCache.get(fingerprint); +        return photoCache.get(masterKeyId);      } -    private static Bitmap loadPhotoFromFingerprint(ContentResolver contentResolver, String fingerprint) { -        if (fingerprint == null) return null; +    public static Bitmap loadPhotoByMasterKeyId(ContentResolver contentResolver, long masterKeyId, +                                                 boolean highRes) { +        if (masterKeyId == -1) { +            return null; +        }          try { -            int rawContactId = findRawContactId(contentResolver, fingerprint); +            long rawContactId = findRawContactId(contentResolver, masterKeyId);              if (rawContactId == -1) {                  return null;              }              Uri rawContactUri = ContentUris.withAppendedId(ContactsContract.RawContacts.CONTENT_URI, rawContactId);              Uri contactUri = ContactsContract.RawContacts.getContactLookupUri(contentResolver, rawContactUri);              InputStream photoInputStream = -                    ContactsContract.Contacts.openContactPhotoInputStream(contentResolver, contactUri); +                    ContactsContract.Contacts.openContactPhotoInputStream(contentResolver, contactUri, highRes);              if (photoInputStream == null) {                  return null;              } @@ -312,12 +278,27 @@ public class ContactHelper {          }      } +    public static final String[] KEYS_TO_CONTACT_PROJECTION = new String[]{ +            KeychainContract.KeyRings.MASTER_KEY_ID, +            KeychainContract.KeyRings.USER_ID, +            KeychainContract.KeyRings.IS_EXPIRED, +            KeychainContract.KeyRings.IS_REVOKED}; + +    public static final int INDEX_MASTER_KEY_ID = 0; +    public static final int INDEX_USER_ID = 1; +    public static final int INDEX_IS_EXPIRED = 2; +    public static final int INDEX_IS_REVOKED = 3; +      /** -     * Write the current Keychain to the contact db +     * Write/Update the current OpenKeychain keys to the contact db       */      public static void writeKeysToContacts(Context context) {          ContentResolver resolver = context.getContentResolver(); -        Set<String> deletedKeys = getRawContactFingerprints(resolver); +        Set<Long> deletedKeys = getRawContactMasterKeyIds(resolver); + +        if (Constants.DEBUG_SYNC_REMOVE_CONTACTS) { +            debugDeleteRawContacts(resolver); +        }  //        ContentProviderClient client = resolver.acquireContentProviderClient(ContactsContract.AUTHORITY_URI);  //        ContentValues values = new ContentValues(); @@ -336,42 +317,41 @@ public class ContactHelper {                  null, null, null);          if (cursor != null) {              while (cursor.moveToNext()) { -                String[] primaryUserId = KeyRing.splitUserId(cursor.getString(INDEX_USER_ID)); -                String fingerprint = KeyFormattingUtils.convertFingerprintToHex(cursor.getBlob(INDEX_FINGERPRINT)); -                deletedKeys.remove(fingerprint); - -                Log.d(Constants.TAG, "fingerprint: " + fingerprint); - -                String keyIdShort = KeyFormattingUtils.convertKeyIdToHexShort(cursor.getLong(INDEX_KEY_ID));                  long masterKeyId = cursor.getLong(INDEX_MASTER_KEY_ID); -                boolean isExpired = !cursor.isNull(INDEX_EXPIRY) -                        && new Date(cursor.getLong(INDEX_EXPIRY) * 1000).before(new Date()); +                String[] userIdSplit = KeyRing.splitUserId(cursor.getString(INDEX_USER_ID)); +                String keyIdShort = KeyFormattingUtils.convertKeyIdToHexShort(cursor.getLong(INDEX_MASTER_KEY_ID)); +                boolean isExpired = cursor.getInt(INDEX_IS_EXPIRED) != 0;                  boolean isRevoked = cursor.getInt(INDEX_IS_REVOKED) > 0; -                int rawContactId = findRawContactId(resolver, fingerprint); -                ArrayList<ContentProviderOperation> ops = new ArrayList<>(); -                Log.d(Constants.TAG, "raw contact id: " + rawContactId); +                Log.d(Constants.TAG, "masterKeyId: " + masterKeyId); + +                deletedKeys.remove(masterKeyId); + +                // get raw contact to this master key id +                long rawContactId = findRawContactId(resolver, masterKeyId); +                Log.d(Constants.TAG, "rawContactId: " + rawContactId); + +                ArrayList<ContentProviderOperation> ops = new ArrayList<>();                  // Do not store expired or revoked keys in contact db - and remove them if they already exist                  if (isExpired || isRevoked) {                      Log.d(Constants.TAG, "Expired or revoked: Deleting " + rawContactId);                      if (rawContactId != -1) { -                        resolver.delete(ContactsContract.RawContacts.CONTENT_URI, ID_SELECTION, -                                new String[]{Integer.toString(rawContactId)}); +                        deleteRawContactById(resolver, rawContactId);                      } -                } else if (primaryUserId[0] != null) { +                } else if (userIdSplit[0] != null) {                      // Create a new rawcontact with corresponding key if it does not exist yet                      if (rawContactId == -1) { -                        Log.d(Constants.TAG, "Insert new raw contact with fingerprint " + fingerprint); +                        Log.d(Constants.TAG, "Insert new raw contact with masterKeyId " + masterKeyId); -                        insertContact(ops, context, fingerprint); +                        insertContact(ops, context, masterKeyId);                          writeContactKey(ops, context, rawContactId, masterKeyId, keyIdShort);                      }                      // We always update the display name (which is derived from primary user id)                      // and email addresses from user id -                    writeContactDisplayName(ops, rawContactId, primaryUserId[0]); +                    writeContactDisplayName(ops, rawContactId, userIdSplit[0]);                      writeContactEmail(ops, resolver, rawContactId, masterKeyId);                      try {                          resolver.applyBatch(ContactsContract.AUTHORITY, ops); @@ -383,42 +363,84 @@ public class ContactHelper {              cursor.close();          } -        // Delete fingerprints that are no longer present in OK -        for (String fingerprint : deletedKeys) { -            resolver.delete(ContactsContract.RawContacts.CONTENT_URI, ACCOUNT_TYPE_AND_SOURCE_ID_SELECTION, -                    new String[]{Constants.ACCOUNT_TYPE, fingerprint}); +        // Delete master key ids that are no longer present in OK +        for (Long masterKeyId : deletedKeys) { +            Log.d(Constants.TAG, "Delete raw contact with masterKeyId " + masterKeyId); +            deleteRawContactByMasterKeyId(resolver, masterKeyId);          }      }      /** -     * @return a set of all key fingerprints currently present in the contact db +     * Delete all raw contacts associated to OpenKeychain. +     * <p/> +     * TODO: Does this work? +     */ +    private static int debugDeleteRawContacts(ContentResolver resolver) { +        Log.d(Constants.TAG, "Deleting all raw contacts associated to OK..."); +        return resolver.delete(ContactsContract.RawContacts.CONTENT_URI, +                ContactsContract.RawContacts.ACCOUNT_TYPE + "=?", +                new String[]{ +                        Constants.ACCOUNT_TYPE +                }); +    } + +    private static int deleteRawContactById(ContentResolver resolver, long rawContactId) { +        return resolver.delete(ContactsContract.RawContacts.CONTENT_URI, +                ContactsContract.RawContacts.ACCOUNT_TYPE + "=? AND " + ContactsContract.RawContacts._ID + "=?", +                new String[]{ +                        Constants.ACCOUNT_TYPE, Long.toString(rawContactId) +                }); +    } + +    private static int deleteRawContactByMasterKeyId(ContentResolver resolver, long masterKeyId) { +        return resolver.delete(ContactsContract.RawContacts.CONTENT_URI, +                ContactsContract.RawContacts.ACCOUNT_TYPE + "=? AND " + ContactsContract.RawContacts.SOURCE_ID + "=?", +                new String[]{ +                        Constants.ACCOUNT_TYPE, Long.toString(masterKeyId) +                }); +    } + +    /** +     * @return a set of all key master key ids currently present in the contact db       */ -    private static Set<String> getRawContactFingerprints(ContentResolver resolver) { -        HashSet<String> result = new HashSet<>(); -        Cursor fingerprints = resolver.query(ContactsContract.RawContacts.CONTENT_URI, SOURCE_ID_PROJECTION, -                ACCOUNT_TYPE_SELECTION, new String[]{Constants.ACCOUNT_TYPE}, null); -        if (fingerprints != null) { -            while (fingerprints.moveToNext()) { -                result.add(fingerprints.getString(0)); +    private static Set<Long> getRawContactMasterKeyIds(ContentResolver resolver) { +        HashSet<Long> result = new HashSet<>(); +        Cursor masterKeyIds = resolver.query(ContactsContract.RawContacts.CONTENT_URI, +                new String[]{ +                        ContactsContract.RawContacts.SOURCE_ID +                }, +                ContactsContract.RawContacts.ACCOUNT_TYPE + "=?", +                new String[]{ +                        Constants.ACCOUNT_TYPE +                }, null); +        if (masterKeyIds != null) { +            while (masterKeyIds.moveToNext()) { +                result.add(masterKeyIds.getLong(0));              } -            fingerprints.close(); +            masterKeyIds.close();          }          return result;      }      /** -     * This will search the contact db for a raw contact with a given fingerprint +     * This will search the contact db for a raw contact with a given master key id       *       * @return raw contact id or -1 if not found       */      @TargetApi(Build.VERSION_CODES.JELLY_BEAN) -    private static int findRawContactId(ContentResolver resolver, String fingerprint) { -        int rawContactId = -1; -        Cursor raw = resolver.query(ContactsContract.RawContacts.CONTENT_URI, ID_PROJECTION, -                ACCOUNT_TYPE_AND_SOURCE_ID_SELECTION, new String[]{Constants.ACCOUNT_TYPE, fingerprint}, null, null); +    private static long findRawContactId(ContentResolver resolver, long masterKeyId) { +        long rawContactId = -1; +        Cursor raw = resolver.query(ContactsContract.RawContacts.CONTENT_URI, +                new String[]{ +                        ContactsContract.RawContacts._ID +                }, +                ContactsContract.RawContacts.ACCOUNT_TYPE + "=? AND " + ContactsContract.RawContacts.SOURCE_ID + "=?", +                new String[]{ +                        Constants.ACCOUNT_TYPE, Long.toString(masterKeyId) +                }, null, null);          if (raw != null) {              if (raw.moveToNext()) { -                rawContactId = raw.getInt(0); +                rawContactId = raw.getLong(0);              }              raw.close();          } @@ -426,13 +448,13 @@ public class ContactHelper {      }      /** -     * Creates a empty raw contact with a given fingerprint +     * Creates a empty raw contact with a given masterKeyId       */ -    private static void insertContact(ArrayList<ContentProviderOperation> ops, Context context, String fingerprint) { +    private static void insertContact(ArrayList<ContentProviderOperation> ops, Context context, long masterKeyId) {          ops.add(ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)                  .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, Constants.ACCOUNT_NAME)                  .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, Constants.ACCOUNT_TYPE) -                .withValue(ContactsContract.RawContacts.SOURCE_ID, fingerprint) +                .withValue(ContactsContract.RawContacts.SOURCE_ID, Long.toString(masterKeyId))                  .build());      } @@ -441,7 +463,7 @@ public class ContactHelper {       * <p/>       * This creates the link to OK in contact details       */ -    private static void writeContactKey(ArrayList<ContentProviderOperation> ops, Context context, int rawContactId, +    private static void writeContactKey(ArrayList<ContentProviderOperation> ops, Context context, long rawContactId,                                          long masterKeyId, String keyIdShort) {          ops.add(referenceRawContact(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI), rawContactId)                  .withValue(ContactsContract.Data.MIMETYPE, Constants.CUSTOM_CONTACT_DATA_MIME_TYPE) @@ -454,16 +476,22 @@ public class ContactHelper {       * Write all known email addresses of a key (derived from user ids) to a given raw contact       */      private static void writeContactEmail(ArrayList<ContentProviderOperation> ops, ContentResolver resolver, -                                          int rawContactId, long masterKeyId) { -        ops.add(selectByRawContactAndItemType(ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI), +                                          long rawContactId, long masterKeyId) { +        ops.add(selectByRawContactAndItemType( +                ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI),                  rawContactId, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE).build());          Cursor ids = resolver.query(UserPackets.buildUserIdsUri(masterKeyId), -                USER_IDS_PROJECTION, NON_REVOKED_SELECTION, null, null); +                new String[]{ +                        UserPackets.USER_ID +                }, +                UserPackets.IS_REVOKED + "=0", +                null, null);          if (ids != null) {              while (ids.moveToNext()) {                  String[] userId = KeyRing.splitUserId(ids.getString(0));                  if (userId[1] != null) { -                    ops.add(referenceRawContact(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI), +                    ops.add(referenceRawContact( +                            ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI),                              rawContactId)                              .withValue(ContactsContract.Data.MIMETYPE,                                      ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE) @@ -475,7 +503,7 @@ public class ContactHelper {          }      } -    private static void writeContactDisplayName(ArrayList<ContentProviderOperation> ops, int rawContactId, +    private static void writeContactDisplayName(ArrayList<ContentProviderOperation> ops, long rawContactId,                                                  String displayName) {          if (displayName != null) {              ops.add(insertOrUpdateForRawContact(ContactsContract.Data.CONTENT_URI, rawContactId, @@ -486,13 +514,13 @@ public class ContactHelper {      }      private static ContentProviderOperation.Builder referenceRawContact(ContentProviderOperation.Builder builder, -                                                                        int rawContactId) { +                                                                        long rawContactId) {          return rawContactId == -1 ?                  builder.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) :                  builder.withValue(ContactsContract.Data.RAW_CONTACT_ID, rawContactId);      } -    private static ContentProviderOperation.Builder insertOrUpdateForRawContact(Uri uri, int rawContactId, +    private static ContentProviderOperation.Builder insertOrUpdateForRawContact(Uri uri, long rawContactId,                                                                                  String itemType) {          if (rawContactId == -1) {              return referenceRawContact(ContentProviderOperation.newInsert(uri), rawContactId).withValue( @@ -503,8 +531,11 @@ public class ContactHelper {      }      private static ContentProviderOperation.Builder selectByRawContactAndItemType( -            ContentProviderOperation.Builder builder, int rawContactId, String itemType) { -        return builder.withSelection(RAW_CONTACT_AND_MIMETYPE_SELECTION, -                new String[]{Integer.toString(rawContactId), itemType}); +            ContentProviderOperation.Builder builder, long rawContactId, String itemType) { +        return builder.withSelection( +                ContactsContract.Data.RAW_CONTACT_ID + "=? AND " + ContactsContract.Data.MIMETYPE + "=?", +                new String[]{ +                        Long.toString(rawContactId), itemType +                });      }  } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java index a36af5c87..44c1e6b6c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java @@ -20,11 +20,7 @@ package org.sufficientlysecure.keychain.util;  import android.content.Context;  import android.content.SharedPreferences; -import android.os.Build; -import org.spongycastle.bcpg.CompressionAlgorithmTags; -import org.spongycastle.bcpg.HashAlgorithmTags; -import org.spongycastle.openpgp.PGPEncryptedData;  import org.sufficientlysecure.keychain.Constants;  import org.sufficientlysecure.keychain.Constants.Pref; @@ -59,12 +55,8 @@ public class Preferences {      }      public void updateSharedPreferences(Context context) { -        // multi-process preferences -        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { -            mSharedPreferences = context.getSharedPreferences("APG.main", Context.MODE_MULTI_PROCESS); -        } else { -            mSharedPreferences = context.getSharedPreferences("APG.main", Context.MODE_PRIVATE); -        } +        // multi-process safe preferences +        mSharedPreferences = context.getSharedPreferences("APG.main", Context.MODE_MULTI_PROCESS);      }      public String getLanguage() { @@ -103,60 +95,6 @@ public class Preferences {          editor.commit();      } -    public int getDefaultEncryptionAlgorithm() { -        return mSharedPreferences.getInt(Constants.Pref.DEFAULT_ENCRYPTION_ALGORITHM, -                PGPEncryptedData.AES_256); -    } - -    public void setDefaultEncryptionAlgorithm(int value) { -        SharedPreferences.Editor editor = mSharedPreferences.edit(); -        editor.putInt(Constants.Pref.DEFAULT_ENCRYPTION_ALGORITHM, value); -        editor.commit(); -    } - -    public int getDefaultHashAlgorithm() { -        return mSharedPreferences.getInt(Constants.Pref.DEFAULT_HASH_ALGORITHM, -                HashAlgorithmTags.SHA256); -    } - -    public void setDefaultHashAlgorithm(int value) { -        SharedPreferences.Editor editor = mSharedPreferences.edit(); -        editor.putInt(Constants.Pref.DEFAULT_HASH_ALGORITHM, value); -        editor.commit(); -    } - -    public int getDefaultMessageCompression() { -        return mSharedPreferences.getInt(Constants.Pref.DEFAULT_MESSAGE_COMPRESSION, -                CompressionAlgorithmTags.ZLIB); -    } - -    public void setDefaultMessageCompression(int value) { -        SharedPreferences.Editor editor = mSharedPreferences.edit(); -        editor.putInt(Constants.Pref.DEFAULT_MESSAGE_COMPRESSION, value); -        editor.commit(); -    } - -    public int getDefaultFileCompression() { -        return mSharedPreferences.getInt(Constants.Pref.DEFAULT_FILE_COMPRESSION, -                CompressionAlgorithmTags.UNCOMPRESSED); -    } - -    public void setDefaultFileCompression(int value) { -        SharedPreferences.Editor editor = mSharedPreferences.edit(); -        editor.putInt(Constants.Pref.DEFAULT_FILE_COMPRESSION, value); -        editor.commit(); -    } - -    public boolean getDefaultAsciiArmor() { -        return mSharedPreferences.getBoolean(Constants.Pref.DEFAULT_ASCII_ARMOR, false); -    } - -    public void setDefaultAsciiArmor(boolean value) { -        SharedPreferences.Editor editor = mSharedPreferences.edit(); -        editor.putBoolean(Constants.Pref.DEFAULT_ASCII_ARMOR, value); -        editor.commit(); -    } -      public boolean getCachedConsolidate() {          return mSharedPreferences.getBoolean(Pref.CACHED_CONSOLIDATE, false);      } @@ -210,6 +148,7 @@ public class Preferences {          }          return servers.toArray(chunks);      } +      public String getPreferredKeyserver() {          return getKeyServers()[0];      } @@ -231,21 +170,12 @@ public class Preferences {          editor.commit();      } -    public void setWriteVersionHeader(boolean conceal) { -        SharedPreferences.Editor editor = mSharedPreferences.edit(); -        editor.putBoolean(Constants.Pref.WRITE_VERSION_HEADER, conceal); -        editor.commit(); -    } - -    public boolean getWriteVersionHeader() { -        return mSharedPreferences.getBoolean(Constants.Pref.WRITE_VERSION_HEADER, false); -    } -      public void setSearchKeyserver(boolean searchKeyserver) {          SharedPreferences.Editor editor = mSharedPreferences.edit();          editor.putBoolean(Pref.SEARCH_KEYSERVER, searchKeyserver);          editor.commit();      } +      public void setSearchKeybase(boolean searchKeybase) {          SharedPreferences.Editor editor = mSharedPreferences.edit();          editor.putBoolean(Pref.SEARCH_KEYBASE, searchKeybase); @@ -253,7 +183,7 @@ public class Preferences {      }      public CloudSearchPrefs getCloudSearchPrefs() { -            return new CloudSearchPrefs(mSharedPreferences.getBoolean(Pref.SEARCH_KEYSERVER, true), +        return new CloudSearchPrefs(mSharedPreferences.getBoolean(Pref.SEARCH_KEYSERVER, true),                  mSharedPreferences.getBoolean(Pref.SEARCH_KEYBASE, true),                  getPreferredKeyserver());      } @@ -301,26 +231,9 @@ public class Preferences {                      }                      setKeyServers(servers.toArray(new String[servers.size()])); - -                    // migrate old uncompressed constant to new one -                    if (mSharedPreferences.getInt(Constants.Pref.DEFAULT_FILE_COMPRESSION, 0) -                            == 0x21070001) { -                        setDefaultFileCompression(CompressionAlgorithmTags.UNCOMPRESSED); -                    } - -                    // migrate away from MD5 -                    if (mSharedPreferences.getInt(Constants.Pref.DEFAULT_HASH_ALGORITHM, 0) -                            == HashAlgorithmTags.MD5) { -                        setDefaultHashAlgorithm(HashAlgorithmTags.SHA256); -                    }                  }                  // fall through                  case 4: { -                    // for compatibility: change from SHA512 to SHA256 -                    if (mSharedPreferences.getInt(Constants.Pref.DEFAULT_HASH_ALGORITHM, 0) -                            == HashAlgorithmTags.SHA512) { -                        setDefaultHashAlgorithm(HashAlgorithmTags.SHA256); -                    }                  }              } | 
