From 067ffa876d4ec4e1f2ee63c851cb3c48f7eb1aa2 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 10 Jun 2014 01:30:20 +0200 Subject: import-log: add OperationResults, use it in ImportKeys operation --- .../keychain/pgp/OperationResultParcel.java | 169 ----------------- .../keychain/pgp/PgpImportExport.java | 45 +++-- .../keychain/provider/ProviderHelper.java | 167 ++++++++++------- .../keychain/service/KeychainIntentService.java | 12 +- .../keychain/service/OperationResultParcel.java | 207 +++++++++++++++++++++ .../keychain/service/OperationResults.java | 63 +++++++ 6 files changed, 408 insertions(+), 255 deletions(-) delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OperationResultParcel.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OperationResultParcel.java deleted file mode 100644 index 216e4b497..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OperationResultParcel.java +++ /dev/null @@ -1,169 +0,0 @@ -package org.sufficientlysecure.keychain.pgp; - -import android.os.Parcel; -import android.os.Parcelable; - -import org.sufficientlysecure.keychain.R; - -import java.util.ArrayList; - -/** Represent the result of an operation. - * - * This class holds a result and the log of an operation. It can be subclassed - * to include typed additional information specific to the operation. To keep - * the class structure (somewhat) simple, this class contains an exhaustive - * list (ie, enum) of all possible log types, which should in all cases be tied - * to string resource ids. - * - */ -public class OperationResultParcel implements Parcelable { - /** Holds the overall result. A value of 0 is considered a success, all - * other values may represent failure or varying degrees of success. */ - final int mResult; - - /// A list of log entries tied to the operation result. - final ArrayList mLog; - - public OperationResultParcel(int result, ArrayList log) { - mResult = result; - mLog = log; - } - - public OperationResultParcel(Parcel source) { - mResult = source.readInt(); - mLog = source.createTypedArrayList(LogEntryParcel.CREATOR); - } - - public boolean isSuccessful() { - return mResult == 0; - } - - /** One entry in the log. */ - public static class LogEntryParcel implements Parcelable { - final LogLevel mLevel; - final LogType mType; - final String[] mParameters; - final int mIndent; - - public LogEntryParcel(LogLevel level, LogType type, String[] parameters, int indent) { - mLevel = level; - mType = type; - mParameters = parameters; - mIndent = indent; - } - public LogEntryParcel(LogLevel level, LogType type, String[] parameters) { - this(level, type, parameters, 0); - } - - public LogEntryParcel(Parcel source) { - mLevel = LogLevel.values()[source.readInt()]; - mType = LogType.values()[source.readInt()]; - mParameters = source.createStringArray(); - mIndent = source.readInt(); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mLevel.ordinal()); - dest.writeInt(mType.ordinal()); - dest.writeStringArray(mParameters); - dest.writeInt(mIndent); - } - - public static final Creator CREATOR = new Creator() { - public LogEntryParcel createFromParcel(final Parcel source) { - return new LogEntryParcel(source); - } - - public LogEntryParcel[] newArray(final int size) { - return new LogEntryParcel[size]; - } - }; - - } - - public static enum LogType { - MSG_IP_APPLY_BATCH (R.string.msg_ip_apply_batch), - MSG_IP_BAD_TYPE_SECRET (R.string.msg_ip_bad_type_secret), - MSG_IP_DELETE_OLD_FAIL (R.string.msg_ip_delete_old_fail), - MSG_IP_DELETE_OLD_OK (R.string.msg_ip_delete_old_ok), - MSG_IP_ENCODE_FAIL (R.string.msg_ip_encode_fail), - MSG_IP_FAIL_IO_EXC (R.string.msg_ip_fail_io_exc), - MSG_IP_FAIL_OP_EX (R.string.msg_ip_fail_op_ex), - MSG_IP_FAIL_REMOTE_EX (R.string.msg_ip_fail_remote_ex), - MSG_IP_IMPORTING (R.string.msg_ip_importing), - MSG_IP_INSERT_KEYRING (R.string.msg_ip_insert_keyring), - MSG_IP_INSERT_SUBKEY (R.string.msg_ip_insert_subkey), - MSG_IP_INSERT_SUBKEYS (R.string.msg_ip_insert_subkeys), - MSG_IP_PRESERVING_SECRET (R.string.msg_ip_preserving_secret), - MSG_IP_REINSERT_SECRET (R.string.msg_ip_reinsert_secret), - MSG_IP_SUCCESS (R.string.msg_ip_success), - MSG_IP_TRUST_RETRIEVE (R.string.msg_ip_trust_retrieve), - MSG_IP_TRUST_USING (R.string.msg_ip_trust_using), - MSG_IP_TRUST_USING_SEC (R.string.msg_ip_trust_using_sec), - MSG_IP_UID_CERT_BAD (R.string.msg_ip_uid_cert_bad), - MSG_IP_UID_CERT_ERROR (R.string.msg_ip_uid_cert_error), - MSG_IP_UID_CERT_GOOD (R.string.msg_ip_uid_cert_good), - MSG_IP_UID_CERTS_UNKNOWN (R.string.msg_ip_uid_certs_unknown), - MSG_IP_UID_CLASSIFYING (R.string.msg_ip_uid_classifying), - MSG_IP_UID_INSERT (R.string.msg_ip_uid_insert), - MSG_IP_UID_PROCESSING (R.string.msg_ip_uid_processing), - MSG_IP_UID_SELF_BAD (R.string.msg_ip_uid_self_bad), - MSG_IP_UID_SELF_GOOD (R.string.msg_ip_uid_self_good), - MSG_IP_UID_SELF_IGNORING_OLD (R.string.msg_ip_uid_self_ignoring_old), - MSG_IP_UID_SELF_NEWER (R.string.msg_ip_uid_self_newer), - MSG_IS_BAD_TYPE_PUBLIC (R.string.msg_is_bad_type_public), - MSG_IS_IMPORTING (R.string.msg_is_importing), - MSG_IS_IMPORTING_SUBKEYS (R.string.msg_is_importing_subkeys), - MSG_IS_IO_EXCPTION (R.string.msg_is_io_excption), - MSG_IS_SUBKEY_NONEXISTENT (R.string.msg_is_subkey_nonexistent), - MSG_IS_SUBKEY_OK (R.string.msg_is_subkey_ok), - MSG_IS_SUBKEY_STRIPPED (R.string.msg_is_subkey_stripped), - MSG_IS_SUCCESS (R.string.msg_is_success), - ; - - private final int mMsgId; - LogType(int msgId) { - mMsgId = msgId; - } - public int getMsgId() { - return mMsgId; - } - } - - /** Enumeration of possible log levels. */ - public static enum LogLevel { - DEBUG, - INFO, - WARN, - /** If any ERROR log entry is included in the result, the overall operation should have failed. */ - ERROR, - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mResult); - dest.writeTypedList(mLog); - } - - public static final Creator CREATOR = new Creator() { - public OperationResultParcel createFromParcel(final Parcel source) { - return new OperationResultParcel(source); - } - - public OperationResultParcel[] newArray(final int size) { - return new OperationResultParcel[size]; - } - }; - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java index 5ce0b11dd..ebc5a7868 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java @@ -33,6 +33,9 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.service.KeychainIntentService; +import org.sufficientlysecure.keychain.service.OperationResultParcel; +import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog; +import org.sufficientlysecure.keychain.service.OperationResults.ImportResult; import org.sufficientlysecure.keychain.util.Log; import java.io.ByteArrayOutputStream; @@ -123,12 +126,9 @@ public class PgpImportExport { } } - /** - * Imports keys from given data. If keyIds is given only those are imported - */ - public Bundle importKeyRings(List entries) + /** Imports keys from given data. If keyIds is given only those are imported */ + public ImportResult importKeyRings(List entries) throws PgpGeneralException, PGPException, IOException { - Bundle returnData = new Bundle(); updateProgress(R.string.progress_importing, 0, 100); @@ -152,14 +152,8 @@ public class PgpImportExport { } } - mProviderHelper.resetLog(); OperationResultParcel result = mProviderHelper.savePublicKeyRing(key); - for(OperationResultParcel.LogEntryParcel loge : result.mLog) { - Log.d(Constants.TAG, - loge.mIndent - + new String(new char[loge.mIndent]).replace("\0", " ") - + mContext.getString(loge.mType.getMsgId(), (Object[]) loge.mParameters)); - } + newKeys += 1; } catch (PgpGeneralException e) { @@ -171,11 +165,30 @@ public class PgpImportExport { updateProgress(position / entries.size() * 100, 100); } - returnData.putInt(KeychainIntentService.RESULT_IMPORT_ADDED, newKeys); - returnData.putInt(KeychainIntentService.RESULT_IMPORT_UPDATED, oldKeys); - returnData.putInt(KeychainIntentService.RESULT_IMPORT_BAD, badKeys); + OperationLog log = mProviderHelper.getLog(); + int resultType; + // Any key imported - overall success + if (newKeys > 0 || oldKeys > 0) { + if (badKeys > 0) { + resultType = ImportResult.RESULT_PARTIAL_WITH_ERRORS; + } else if (log.containsWarnings()) { + resultType = ImportResult.RESULT_OK_WITH_WARNINGS; + } else if (newKeys > 0 && oldKeys > 0) { + resultType = ImportResult.RESULT_OK_BOTHKEYS; + } else if (newKeys > 0) { + resultType = ImportResult.RESULT_OK_NEWKEYS; + } else { + resultType = ImportResult.RESULT_OK_UPDATED; + } + // No keys imported, overall failure + } else if (badKeys > 0) { + resultType = ImportResult.RESULT_FAIL_ERROR; + } else { + resultType = ImportResult.RESULT_FAIL_NOTHING; + } + + return new ImportResult(resultType, log, newKeys, oldKeys, badKeys); - return returnData; } public Bundle exportKeyRings(ArrayList publicKeyRingMasterIds, 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 5c8bf6752..ab4672a98 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -29,9 +29,10 @@ import android.support.v4.util.LongSparseArray; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.pgp.KeyRing; -import org.sufficientlysecure.keychain.pgp.OperationResultParcel; -import org.sufficientlysecure.keychain.pgp.OperationResultParcel.LogType; -import org.sufficientlysecure.keychain.pgp.OperationResultParcel.LogLevel; +import org.sufficientlysecure.keychain.service.OperationResultParcel; +import org.sufficientlysecure.keychain.service.OperationResultParcel.LogType; +import org.sufficientlysecure.keychain.service.OperationResultParcel.LogLevel; +import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog; import org.sufficientlysecure.keychain.pgp.PgpHelper; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; @@ -48,6 +49,7 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; import org.sufficientlysecure.keychain.remote.AccountSettings; import org.sufficientlysecure.keychain.remote.AppSettings; +import org.sufficientlysecure.keychain.service.OperationResults; import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.Log; @@ -61,18 +63,27 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +/** This class contains high level methods for database access. Despite its + * name, it is not only a helper but actually the main interface for all + * synchronous database operations. + * + * Operations in this class write logs (TODO). These can be obtained from the + * OperationResultParcel return values directly, but are also accumulated over + * the lifetime of the executing ProviderHelper object unless the resetLog() + * method is called to start a new one specifically. + * + */ public class ProviderHelper { private final Context mContext; private final ContentResolver mContentResolver; - private final ArrayList mLog; + private OperationLog mLog; private int mIndent; public ProviderHelper(Context context) { - this(context, new ArrayList(), 0); + this(context, new OperationLog(), 0); } - public ProviderHelper(Context context, ArrayList log, - int indent) { + public ProviderHelper(Context context, OperationLog log, int indent) { mContext = context; mContentResolver = context.getContentResolver(); mLog = log; @@ -81,11 +92,16 @@ public class ProviderHelper { public void resetLog() { if(mLog != null) { - mLog.clear(); + // Start a new log (leaving the old one intact) + mLog = new OperationLog(); mIndent = 0; } } + public OperationLog getLog() { + return mLog; + } + public static class NotFoundException extends Exception { public NotFoundException() { } @@ -97,12 +113,12 @@ public class ProviderHelper { public void log(LogLevel level, LogType type) { if(mLog != null) { - mLog.add(new OperationResultParcel.LogEntryParcel(level, type, null, mIndent)); + mLog.add(level, type, null, mIndent); } } public void log(LogLevel level, LogType type, String[] parameters) { if(mLog != null) { - mLog.add(new OperationResultParcel.LogEntryParcel(level, type, parameters, mIndent)); + mLog.add(level, type, parameters, mIndent); } } @@ -258,6 +274,9 @@ public class ProviderHelper { return new OperationResultParcel(1, mLog); } + // Canonicalize this key, to assert a number of assumptions made about the key. + keyRing = keyRing.canonicalize(mLog); + UncachedPublicKey masterKey = keyRing.getPublicKey(); long masterKeyId = masterKey.getKeyId(); log(LogLevel.INFO, LogType.MSG_IP_IMPORTING, @@ -282,34 +301,80 @@ public class ProviderHelper { log(LogLevel.DEBUG, LogType.MSG_IP_DELETE_OLD_FAIL); } - // insert new version of this keyRing - ContentValues values = new ContentValues(); - values.put(KeyRingData.MASTER_KEY_ID, masterKeyId); try { - values.put(KeyRingData.KEY_RING_DATA, keyRing.getEncoded()); - } catch (IOException e) { - log(LogLevel.ERROR, LogType.MSG_IP_ENCODE_FAIL); - return new OperationResultParcel(1, mLog); - } - - // save all keys and userIds included in keyRing object in database - ArrayList operations = new ArrayList(); - try { + // save all keys and userIds included in keyRing object in database + ArrayList operations = new ArrayList(); log(LogLevel.INFO, LogType.MSG_IP_INSERT_KEYRING); - Uri uri = KeyRingData.buildPublicKeyRingUri(Long.toString(masterKeyId)); - operations.add(ContentProviderOperation.newInsert(uri).withValues(values).build()); + { // insert keyring + // insert new version of this keyRing + ContentValues values = new ContentValues(); + values.put(KeyRingData.MASTER_KEY_ID, masterKeyId); + try { + values.put(KeyRingData.KEY_RING_DATA, keyRing.getEncoded()); + } catch (IOException e) { + log(LogLevel.ERROR, LogType.MSG_IP_ENCODE_FAIL); + return new OperationResultParcel(1, mLog); + } + + Uri uri = KeyRingData.buildPublicKeyRingUri(Long.toString(masterKeyId)); + operations.add(ContentProviderOperation.newInsert(uri).withValues(values).build()); + } log(LogLevel.INFO, LogType.MSG_IP_INSERT_SUBKEYS); mIndent += 1; - int rank = 0; - for (UncachedPublicKey key : new IterableIterator(keyRing.getPublicKeys())) { - log(LogLevel.DEBUG, LogType.MSG_IP_INSERT_SUBKEY, new String[] { - PgpKeyHelper.convertKeyIdToHex(key.getKeyId()) - }); - operations.add(buildPublicKeyOperations(masterKeyId, key, rank)); - ++rank; + { // insert subkeys + Uri uri = Keys.buildKeysUri(Long.toString(masterKeyId)); + int rank = 0; + for (UncachedPublicKey key : new IterableIterator(keyRing.getPublicKeys())) { + log(LogLevel.DEBUG, LogType.MSG_IP_SUBKEY, new String[]{ + PgpKeyHelper.convertKeyIdToHex(key.getKeyId()) + }); + mIndent += 1; + + ContentValues values = new ContentValues(); + values.put(Keys.MASTER_KEY_ID, masterKeyId); + values.put(Keys.RANK, rank); + + values.put(Keys.KEY_ID, key.getKeyId()); + values.put(Keys.KEY_SIZE, key.getBitStrength()); + values.put(Keys.ALGORITHM, key.getAlgorithm()); + values.put(Keys.FINGERPRINT, key.getFingerprint()); + + boolean c = key.canCertify(), s = key.canSign(), e = key.canEncrypt(); + values.put(Keys.CAN_CERTIFY, c); + values.put(Keys.CAN_SIGN, s); + values.put(Keys.CAN_ENCRYPT, e); + values.put(Keys.IS_REVOKED, key.isRevoked()); + log(LogLevel.DEBUG, LogType.MSG_IP_SUBKEY_FLAGS, new String[] { "X" }); + + Date creation = key.getCreationTime(); + values.put(Keys.CREATION, creation.getTime() / 1000); + if (creation.after(new Date())) { + log(LogLevel.ERROR, LogType.MSG_IP_SUBKEY_FUTURE, new String[] { + creation.toString() + }); + return new OperationResultParcel(1, mLog); + } + Date expiryDate = key.getExpiryTime(); + if (expiryDate != null) { + values.put(Keys.EXPIRY, expiryDate.getTime() / 1000); + if (key.isExpired()) { + log(LogLevel.INFO, LogType.MSG_IP_SUBKEY_EXPIRED, new String[] { + expiryDate.toString() + }); + } else { + log(LogLevel.DEBUG, LogType.MSG_IP_SUBKEY_EXPIRES, new String[] { + expiryDate.toString() + }); + } + } + + operations.add(ContentProviderOperation.newInsert(uri).withValues(values).build()); + ++rank; + mIndent -= 1; + } } mIndent -= 1; @@ -374,7 +439,12 @@ public class ProviderHelper { // save certificate as primary self-cert item.selfCert = cert; item.isPrimary = cert.isPrimaryUserId(); - item.isRevoked = cert.isRevocation(); + if (cert.isRevocation()) { + item.isRevoked = true; + log(LogLevel.INFO, LogType.MSG_IP_UID_REVOKED); + } else { + item.isRevoked = false; + } } @@ -500,7 +570,7 @@ public class ProviderHelper { long masterKeyId = keyRing.getMasterKeyId(); log(LogLevel.INFO, LogType.MSG_IS_IMPORTING, - new String[]{ Long.toString(masterKeyId) }); + new String[]{Long.toString(masterKeyId)}); // save secret keyring try { @@ -575,37 +645,6 @@ public class ProviderHelper { saveSecretKeyRing(secRing); } - /** - * Build ContentProviderOperation to add PGPPublicKey to database corresponding to a keyRing - */ - private ContentProviderOperation - buildPublicKeyOperations(long masterKeyId, UncachedPublicKey key, int rank) throws IOException { - - ContentValues values = new ContentValues(); - values.put(Keys.MASTER_KEY_ID, masterKeyId); - values.put(Keys.RANK, rank); - - values.put(Keys.KEY_ID, key.getKeyId()); - values.put(Keys.KEY_SIZE, key.getBitStrength()); - values.put(Keys.ALGORITHM, key.getAlgorithm()); - values.put(Keys.FINGERPRINT, key.getFingerprint()); - - values.put(Keys.CAN_CERTIFY, key.canCertify()); - values.put(Keys.CAN_SIGN, key.canSign()); - values.put(Keys.CAN_ENCRYPT, key.canEncrypt()); - values.put(Keys.IS_REVOKED, key.maybeRevoked()); - - values.put(Keys.CREATION, key.getCreationTime().getTime() / 1000); - Date expiryDate = key.getExpiryTime(); - if (expiryDate != null) { - values.put(Keys.EXPIRY, expiryDate.getTime() / 1000); - } - - Uri uri = Keys.buildKeysUri(Long.toString(masterKeyId)); - - return ContentProviderOperation.newInsert(uri).withValues(values).build(); - } - /** * Build ContentProviderOperation to add PGPPublicKey to database corresponding to a keyRing */ diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java index 498b963f2..f2231fbb5 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -174,14 +174,11 @@ public class KeychainIntentService extends IntentService public static final String RESULT_DECRYPTED_BYTES = "decrypted_data"; public static final String RESULT_DECRYPT_VERIFY_RESULT = "signature"; - // import - public static final String RESULT_IMPORT_ADDED = "added"; - public static final String RESULT_IMPORT_UPDATED = "updated"; - public static final String RESULT_IMPORT_BAD = "bad"; - // export public static final String RESULT_EXPORT = "exported"; + public static final String RESULT = "result"; + Messenger mMessenger; private boolean mIsCanceled; @@ -648,7 +645,10 @@ public class KeychainIntentService extends IntentService List entries = data.getParcelableArrayList(IMPORT_KEY_LIST); PgpImportExport pgpImportExport = new PgpImportExport(this, this); - Bundle resultData = pgpImportExport.importKeyRings(entries); + OperationResults.ImportResult result = pgpImportExport.importKeyRings(entries); + + Bundle resultData = new Bundle(); + resultData.putParcelable(RESULT, result); sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData); } catch (Exception e) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java new file mode 100644 index 000000000..a1a1d0067 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -0,0 +1,207 @@ +package org.sufficientlysecure.keychain.service; + +import android.os.Parcel; +import android.os.Parcelable; + +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.util.IterableIterator; + +import java.util.ArrayList; + +/** Represent the result of an operation. + * + * This class holds a result and the log of an operation. It can be subclassed + * to include typed additional information specific to the operation. To keep + * the class structure (somewhat) simple, this class contains an exhaustive + * list (ie, enum) of all possible log types, which should in all cases be tied + * to string resource ids. + * + */ +public class OperationResultParcel implements Parcelable { + /** Holds the overall result, the number specifying varying degrees of success. + * Values smaller than 100 are considered an overall success. */ + final int mResult; + + public static final int RESULT_OK = 0; + public static final int RESULT_ERROR = 100; + + /// A list of log entries tied to the operation result. + final OperationLog mLog; + + public OperationResultParcel(int result, OperationLog log) { + mResult = result; + mLog = log; + } + + public OperationResultParcel(Parcel source) { + mResult = source.readInt(); + mLog = new OperationLog(); + mLog.addAll(source.createTypedArrayList(LogEntryParcel.CREATOR)); + } + + public int getResult() { + return mResult; + } + + public boolean isSuccessful() { + return mResult < 100; + } + + public OperationLog getLog() { + return mLog; + } + + /** One entry in the log. */ + public static class LogEntryParcel implements Parcelable { + public final LogLevel mLevel; + public final LogType mType; + public final String[] mParameters; + public final int mIndent; + + public LogEntryParcel(LogLevel level, LogType type, String[] parameters, int indent) { + mLevel = level; + mType = type; + mParameters = parameters; + mIndent = indent; + } + public LogEntryParcel(LogLevel level, LogType type, String[] parameters) { + this(level, type, parameters, 0); + } + + public LogEntryParcel(Parcel source) { + mLevel = LogLevel.values()[source.readInt()]; + mType = LogType.values()[source.readInt()]; + mParameters = source.createStringArray(); + mIndent = source.readInt(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mLevel.ordinal()); + dest.writeInt(mType.ordinal()); + dest.writeStringArray(mParameters); + dest.writeInt(mIndent); + } + + public static final Creator CREATOR = new Creator() { + public LogEntryParcel createFromParcel(final Parcel source) { + return new LogEntryParcel(source); + } + + public LogEntryParcel[] newArray(final int size) { + return new LogEntryParcel[size]; + } + }; + + } + + public static enum LogType { + MSG_IP_APPLY_BATCH (R.string.msg_ip_apply_batch), + MSG_IP_BAD_TYPE_SECRET (R.string.msg_ip_bad_type_secret), + MSG_IP_DELETE_OLD_FAIL (R.string.msg_ip_delete_old_fail), + MSG_IP_DELETE_OLD_OK (R.string.msg_ip_delete_old_ok), + MSG_IP_ENCODE_FAIL (R.string.msg_ip_encode_fail), + MSG_IP_FAIL_IO_EXC (R.string.msg_ip_fail_io_exc), + MSG_IP_FAIL_OP_EX (R.string.msg_ip_fail_op_ex), + MSG_IP_FAIL_REMOTE_EX (R.string.msg_ip_fail_remote_ex), + MSG_IP_IMPORTING (R.string.msg_ip_importing), + MSG_IP_INSERT_KEYRING (R.string.msg_ip_insert_keyring), + MSG_IP_INSERT_SUBKEYS (R.string.msg_ip_insert_subkeys), + MSG_IP_PRESERVING_SECRET (R.string.msg_ip_preserving_secret), + MSG_IP_REINSERT_SECRET (R.string.msg_ip_reinsert_secret), + MSG_IP_SUBKEY (R.string.msg_ip_subkey), + MSG_IP_SUBKEY_EXPIRED (R.string.msg_ip_subkey_expired), + MSG_IP_SUBKEY_EXPIRES (R.string.msg_ip_subkey_expires), + MSG_IP_SUBKEY_FLAGS (R.string.msg_ip_subkey_flags), + MSG_IP_SUBKEY_FUTURE (R.string.msg_ip_subkey_future), + MSG_IP_SUCCESS (R.string.msg_ip_success), + MSG_IP_TRUST_RETRIEVE (R.string.msg_ip_trust_retrieve), + MSG_IP_TRUST_USING (R.string.msg_ip_trust_using), + MSG_IP_TRUST_USING_SEC (R.string.msg_ip_trust_using_sec), + MSG_IP_UID_CERT_BAD (R.string.msg_ip_uid_cert_bad), + MSG_IP_UID_CERT_ERROR (R.string.msg_ip_uid_cert_error), + MSG_IP_UID_CERT_GOOD (R.string.msg_ip_uid_cert_good), + MSG_IP_UID_CERTS_UNKNOWN (R.string.msg_ip_uid_certs_unknown), + MSG_IP_UID_CLASSIFYING (R.string.msg_ip_uid_classifying), + MSG_IP_UID_INSERT (R.string.msg_ip_uid_insert), + MSG_IP_UID_PROCESSING (R.string.msg_ip_uid_processing), + MSG_IP_UID_REVOKED (R.string.msg_ip_uid_revoked), + MSG_IP_UID_SELF_BAD (R.string.msg_ip_uid_self_bad), + MSG_IP_UID_SELF_GOOD (R.string.msg_ip_uid_self_good), + MSG_IP_UID_SELF_IGNORING_OLD (R.string.msg_ip_uid_self_ignoring_old), + MSG_IP_UID_SELF_NEWER (R.string.msg_ip_uid_self_newer), + MSG_IS_BAD_TYPE_PUBLIC (R.string.msg_is_bad_type_public), + MSG_IS_IMPORTING (R.string.msg_is_importing), + MSG_IS_IMPORTING_SUBKEYS (R.string.msg_is_importing_subkeys), + MSG_IS_IO_EXCPTION (R.string.msg_is_io_excption), + MSG_IS_SUBKEY_NONEXISTENT (R.string.msg_is_subkey_nonexistent), + MSG_IS_SUBKEY_OK (R.string.msg_is_subkey_ok), + MSG_IS_SUBKEY_STRIPPED (R.string.msg_is_subkey_stripped), + MSG_IS_SUCCESS (R.string.msg_is_success), + ; + + private final int mMsgId; + LogType(int msgId) { + mMsgId = msgId; + } + public int getMsgId() { + return mMsgId; + } + } + + /** Enumeration of possible log levels. */ + public static enum LogLevel { + OK, + DEBUG, + INFO, + WARN, + /** If any ERROR log entry is included in the result, the overall operation should have failed. */ + ERROR, + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mResult); + dest.writeTypedList(mLog); + } + + public static final Creator CREATOR = new Creator() { + public OperationResultParcel createFromParcel(final Parcel source) { + return new OperationResultParcel(source); + } + + public OperationResultParcel[] newArray(final int size) { + return new OperationResultParcel[size]; + } + }; + + public static class OperationLog extends ArrayList { + + /// Simple convenience method + public void add(LogLevel level, LogType type, String[] parameters, int indent) { + add(new OperationResultParcel.LogEntryParcel(level, type, parameters, indent)); + } + + public boolean containsWarnings() { + int warn = LogLevel.WARN.ordinal(); + for(LogEntryParcel entry : new IterableIterator(iterator())) { + if (entry.mLevel.ordinal() >= warn) { + return true; + } + } + return false; + } + + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java new file mode 100644 index 000000000..e08b50500 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java @@ -0,0 +1,63 @@ +package org.sufficientlysecure.keychain.service; + +import android.os.Parcel; + +public abstract class OperationResults { + + public static class ImportResult extends OperationResultParcel { + + public final int mNewKeys, mUpdatedKeys, mBadKeys; + + // Operation ok, at least one new key (no warnings) + public static final int RESULT_OK_NEWKEYS = 1; + // Operation ok, at least one new and one updated key (no warnings) + public static final int RESULT_OK_BOTHKEYS = 2; + // Operation ok, no new keys but upated ones (no warnings) + public static final int RESULT_OK_UPDATED = 3; + // Operation ok, but with warnings + public static final int RESULT_OK_WITH_WARNINGS = 4; + + // Operation partially ok, but at least one key failed! + public static final int RESULT_PARTIAL_WITH_ERRORS = 50; + + // Operation failed, errors thrown and no new keys imported + public static final int RESULT_FAIL_ERROR = 100; + // Operation failed, no keys to import... + public static final int RESULT_FAIL_NOTHING = 101; + + public ImportResult(Parcel source) { + super(source); + mNewKeys = source.readInt(); + mUpdatedKeys = source.readInt(); + mBadKeys = source.readInt(); + } + + public ImportResult(int result, OperationLog log, + int newKeys, int updatedKeys, int badKeys) { + super(result, log); + mNewKeys = newKeys; + mUpdatedKeys = updatedKeys; + mBadKeys = badKeys; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeInt(mNewKeys); + dest.writeInt(mUpdatedKeys); + dest.writeInt(mBadKeys); + } + + public static Creator CREATOR = new Creator() { + public ImportResult createFromParcel(final Parcel source) { + return new ImportResult(source); + } + + public ImportResult[] newArray(final int size) { + return new ImportResult[size]; + } + }; + + } + +} -- cgit v1.2.3 From cdc61c43927f11835fffa090ccb045d206728692 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 10 Jun 2014 01:51:16 +0200 Subject: canonicalize: first step(s) --- .../keychain/keyimport/ImportKeysListEntry.java | 2 +- .../keychain/pgp/UncachedKeyRing.java | 55 ++++++++++++++++++++++ .../keychain/pgp/UncachedPublicKey.java | 12 ++++- 3 files changed, 66 insertions(+), 3 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java index c43f72235..7a7475603 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java @@ -229,7 +229,7 @@ public class ImportKeysListEntry implements Serializable, Parcelable { this.keyId = key.getKeyId(); this.keyIdHex = PgpKeyHelper.convertKeyIdToHex(keyId); - this.revoked = key.maybeRevoked(); + this.revoked = key.isRevoked(); this.fingerprintHex = PgpKeyHelper.convertFingerprintToHex(key.getFingerprint()); this.bitStrength = key.getBitStrength(); final int algorithm = key.getAlgorithm(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java index 1264c8c36..624b7d068 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java @@ -7,9 +7,14 @@ 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.PGPUtil; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; +import org.sufficientlysecure.keychain.service.OperationResultParcel; +import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog; +import org.sufficientlysecure.keychain.service.OperationResultParcel.LogLevel; +import org.sufficientlysecure.keychain.service.OperationResultParcel.LogType; import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.Log; @@ -169,4 +174,54 @@ public class UncachedKeyRing { return result; } + /** "Canonicalizes" a key, removing inconsistencies in the process. This operation can be + * applied to public keyrings only. + * + * More specifically: + * - Remove all non-verifying self-certificates + * - Remove all expired self-certificates + * - Remove all certificates flagged as "local" + * - Remove all certificates which are superseded by a newer one on the same target + * + * After this cleaning, a number of checks are done: + * - See if each subkey retains a valid self certificate + * - See if each user id retains a valid self certificate + * + * This operation writes an OperationLog which can be used as part of a OperationResultParcel. + * + * If any of these checks fail, the operation as a whole fails and the keyring is declared + * unusable. (TODO: allow forcing of import?) + * + * TODO implement + * + * @return A canonicalized key + * + */ + public UncachedKeyRing canonicalize(OperationLog log) { + if(isSecret()) { + throw new RuntimeException("Tried to canonicalize non-secret keyring. " + + "This is a programming error and should never happen!"); + } + + // dummy + log.add(LogLevel.INFO, LogType.MSG_IP_BAD_TYPE_SECRET, null, 0); + + /* + // Remove all non-verifying self certificates + for (PGPPublicKey key : new IterableIterator(mRing.getPublicKeys())) { + + for (PGPSignature sig : new IterableIterator( + key.getSignaturesOfType(isMasterKey() ? PGPSignature.KEY_REVOCATION + : PGPSignature.SUBKEY_REVOCATION))) { + return true; + } + + }*/ + + return this; + + + } + + } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java index 108c8c8c3..33db7771b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java @@ -2,6 +2,7 @@ package org.sufficientlysecure.keychain.pgp; import org.spongycastle.bcpg.SignatureSubpacketTags; import org.spongycastle.bcpg.sig.KeyFlags; +import org.spongycastle.openpgp.PGPException; import org.spongycastle.openpgp.PGPPublicKey; import org.spongycastle.openpgp.PGPSignature; import org.spongycastle.openpgp.PGPSignatureSubpacketVector; @@ -9,6 +10,7 @@ import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProv import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.util.IterableIterator; +import java.security.SignatureException; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; @@ -28,8 +30,13 @@ public class UncachedPublicKey { } /** The revocation signature is NOT checked here, so this may be false! */ - public boolean maybeRevoked() { - return mPublicKey.isRevoked(); + public boolean isRevoked() { + for (PGPSignature sig : new IterableIterator( + mPublicKey.getSignaturesOfType(isMasterKey() ? PGPSignature.KEY_REVOCATION + : PGPSignature.SUBKEY_REVOCATION))) { + return true; + } + return false; } public Date getCreationTime() { @@ -193,4 +200,5 @@ public class UncachedPublicKey { } }; } + } -- cgit v1.2.3 From d73a3e2fa89663e133ce4750e4aba86354c8b252 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 10 Jun 2014 01:52:38 +0200 Subject: import-log: use supertoast in ImportKeyActivity --- .../keychain/ui/ImportKeysActivity.java | 113 ++++++++++++++++----- 1 file changed, 87 insertions(+), 26 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java index 48602aaa1..d1869454d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java @@ -38,7 +38,10 @@ import android.view.View; import android.view.View.OnClickListener; import android.widget.ArrayAdapter; -import com.devspark.appmsg.AppMsg; +import com.github.johnpersano.supertoasts.SuperCardToast; +import com.github.johnpersano.supertoasts.SuperToast; +import com.github.johnpersano.supertoasts.util.OnClickWrapper; +import com.github.johnpersano.supertoasts.util.Style; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; @@ -47,7 +50,7 @@ import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; -import org.sufficientlysecure.keychain.ui.dialog.BadImportKeyDialogFragment; +import org.sufficientlysecure.keychain.service.OperationResults.ImportResult; import org.sufficientlysecure.keychain.util.Log; import java.util.ArrayList; @@ -135,6 +138,7 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O } handleActions(savedInstanceState, getIntent()); + } protected void handleActions(Bundle savedInstanceState, Intent intent) { @@ -331,8 +335,11 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O public void loadFromFingerprint(Bundle savedInstanceState, String fingerprint) { if (fingerprint == null || fingerprint.length() < 40) { - AppMsg.makeText(this, R.string.import_qr_code_too_short_fingerprint, - AppMsg.STYLE_ALERT).show(); + SuperCardToast toast = SuperCardToast.create(this, + getString(R.string.import_qr_code_too_short_fingerprint), + SuperToast.Duration.LONG); + toast.setBackground(SuperToast.Background.RED); + toast.show(); return; } @@ -368,34 +375,84 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { // get returned data bundle Bundle returnData = message.getData(); + final ImportResult result = + returnData.getParcelable(KeychainIntentService.RESULT); + + // , make pessimistic assumptions + String str = Integer.toString(result.getResult()); + int duration = 0, color = Style.RED; + + switch(result.getResult()) { + case ImportResult.RESULT_OK_NEWKEYS: + color = Style.GREEN; + duration = SuperToast.Duration.LONG; + str = getResources().getQuantityString( + R.plurals.keys_added, result.mNewKeys, result.mNewKeys); + break; + + case ImportResult.RESULT_OK_UPDATED: + color = Style.GREEN; + duration = SuperToast.Duration.LONG; + str = getResources().getQuantityString( + R.plurals.keys_updated, result.mNewKeys, result.mNewKeys); + break; + + case ImportResult.RESULT_OK_BOTHKEYS: + color = Style.GREEN; + duration = SuperToast.Duration.LONG; + str = getResources().getQuantityString( + R.plurals.keys_added_and_updated_1, result.mNewKeys, result.mNewKeys); + str += getResources().getQuantityString( + R.plurals.keys_added_and_updated_2, result.mUpdatedKeys, result.mUpdatedKeys); + break; + + case ImportResult.RESULT_OK_WITH_WARNINGS: + str = "ok with warnings"; + color = Style.ORANGE; + break; + + case ImportResult.RESULT_PARTIAL_WITH_ERRORS: + str = "partial with errors"; + color = Style.ORANGE; + break; + + case ImportResult.RESULT_FAIL_ERROR: + str = "fail error"; + color = Style.RED; + break; + + case ImportResult.RESULT_FAIL_NOTHING: + str = getString(R.string.no_keys_added_or_updated); + color = Style.RED; + break; - int added = returnData.getInt(KeychainIntentService.RESULT_IMPORT_ADDED); - int updated = returnData - .getInt(KeychainIntentService.RESULT_IMPORT_UPDATED); - int bad = returnData.getInt(KeychainIntentService.RESULT_IMPORT_BAD); - String toastMessage; - if (added > 0 && updated > 0) { - String addedStr = getResources().getQuantityString( - R.plurals.keys_added_and_updated_1, added, added); - String updatedStr = getResources().getQuantityString( - R.plurals.keys_added_and_updated_2, updated, updated); - toastMessage = addedStr + updatedStr; - } else if (added > 0) { - toastMessage = getResources().getQuantityString(R.plurals.keys_added, - added, added); - } else if (updated > 0) { - toastMessage = getResources().getQuantityString(R.plurals.keys_updated, - updated, updated); - } else { - toastMessage = getString(R.string.no_keys_added_or_updated); } - AppMsg.makeText(ImportKeysActivity.this, toastMessage, AppMsg.STYLE_INFO) - .show(); + SuperCardToast toast = new SuperCardToast(ImportKeysActivity.this, + SuperToast.Type.BUTTON, Style.getStyle(color, SuperToast.Animations.POPUP)); + toast.setText(str); + toast.setDuration(duration); + toast.setIndeterminate(duration == 0); + toast.setButtonText("View log"); + toast.setSwipeToDismiss(true); + toast.setOnClickWrapper(new OnClickWrapper("supercardtoast", + new SuperToast.OnClickListener() { + @Override + public void onClick(View view, Parcelable token) { + // Intent intent = new Intent( + // ImportKeysActivity.this, LogDisplayActivity.class); + // intent.putExtra(LogDisplayFragment.EXTRA_RESULT, result); + // startActivity(intent); + } + })); + toast.show(); + + /* if (bad > 0) { BadImportKeyDialogFragment badImportKeyDialogFragment = BadImportKeyDialogFragment.newInstance(bad); badImportKeyDialogFragment.show(getSupportFragmentManager(), "badKeyDialog"); } + */ if (ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN.equals(getIntent().getAction())) { ImportKeysActivity.this.setResult(Activity.RESULT_OK, mPendingIntentData); @@ -483,7 +540,11 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O startService(intent); } else { - AppMsg.makeText(this, R.string.error_nothing_import, AppMsg.STYLE_ALERT).show(); + SuperCardToast toast = SuperCardToast.create(this, + getString(R.string.error_nothing_import), + SuperToast.Duration.LONG); + toast.setBackground(SuperToast.Background.RED); + toast.show(); } } -- cgit v1.2.3 From 7324bfcb536b4975365ab9affa0a71ef29e29b93 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 10 Jun 2014 01:53:25 +0200 Subject: import-log: add LogDisplay activity --- .../keychain/ui/ImportKeysActivity.java | 8 +- .../keychain/ui/LogDisplayActivity.java | 17 +++++ .../keychain/ui/LogDisplayFragment.java | 86 ++++++++++++++++++++++ 3 files changed, 107 insertions(+), 4 deletions(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java index d1869454d..90d772bd1 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java @@ -438,10 +438,10 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O new SuperToast.OnClickListener() { @Override public void onClick(View view, Parcelable token) { - // Intent intent = new Intent( - // ImportKeysActivity.this, LogDisplayActivity.class); - // intent.putExtra(LogDisplayFragment.EXTRA_RESULT, result); - // startActivity(intent); + Intent intent = new Intent( + ImportKeysActivity.this, LogDisplayActivity.class); + intent.putExtra(LogDisplayFragment.EXTRA_RESULT, result); + startActivity(intent); } })); toast.show(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java new file mode 100644 index 000000000..28c120d01 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java @@ -0,0 +1,17 @@ +package org.sufficientlysecure.keychain.ui; + +import android.os.Bundle; +import android.support.v7.app.ActionBarActivity; + +import org.sufficientlysecure.keychain.R; + +public class LogDisplayActivity extends ActionBarActivity { + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.log_display_activity); + } + +} \ No newline at end of file diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java new file mode 100644 index 000000000..36d3e932e --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java @@ -0,0 +1,86 @@ +package org.sufficientlysecure.keychain.ui; + +import android.content.Context; +import android.content.Intent; +import android.graphics.Color; +import android.os.Bundle; +import android.support.v4.app.ListFragment; +import android.util.TypedValue; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.TextView; + +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.service.OperationResultParcel; +import org.sufficientlysecure.keychain.service.OperationResultParcel.LogEntryParcel; + +import java.util.ArrayList; + +public class LogDisplayFragment extends ListFragment { + + LogAdapter mAdapter; + + public static final String EXTRA_RESULT = "log"; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + Intent intent = getActivity().getIntent(); + if (intent.getExtras() == null || !intent.getExtras().containsKey(EXTRA_RESULT)) { + getActivity().finish(); + return; + } + + OperationResultParcel result = intent.getParcelableExtra(EXTRA_RESULT); + if (result == null) { + getActivity().finish(); + return; + } + + mAdapter = new LogAdapter(getActivity(), result.getLog()); + setListAdapter(mAdapter); + + } + + private class LogAdapter extends ArrayAdapter { + + private LayoutInflater mInflater; + private int dipFactor; + + public LogAdapter(Context context, ArrayList log) { + super(context, R.layout.log_display_item, log); + mInflater = LayoutInflater.from(getContext()); + dipFactor = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, + (float) 6, getResources().getDisplayMetrics()); + + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + LogEntryParcel entry = getItem(position); + TextView text; + if (convertView == null) { + convertView = mInflater.inflate(R.layout.log_display_item, parent, false); + text = (TextView) convertView.findViewById(R.id.log_text); + convertView.setTag(text); + } else { + text = (TextView) convertView.getTag(); + } + + text.setPadding(entry.mIndent*dipFactor, 0, 0, 0); + text.setText(getResources().getString(entry.mType.getMsgId(), (Object[]) entry.mParameters)); + switch (entry.mLevel) { + case DEBUG: text.setTextColor(Color.GRAY); break; + case INFO: text.setTextColor(Color.BLACK); break; + case WARN: text.setTextColor(Color.YELLOW); break; + case ERROR: text.setTextColor(Color.RED); break; + } + + return convertView; + } + + } +} -- cgit v1.2.3 From eac582a313c779e77b0fd67358417d512680facd Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 10 Jun 2014 13:52:05 +0200 Subject: import-log: some cosmetics --- .../java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java | 5 ++++- .../java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java | 3 ++- .../java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java index 624b7d068..7853d0b00 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java @@ -204,7 +204,8 @@ public class UncachedKeyRing { } // dummy - log.add(LogLevel.INFO, LogType.MSG_IP_BAD_TYPE_SECRET, null, 0); + log.add(LogLevel.START, LogType.MSG_KC, + new String[] { PgpKeyHelper.convertKeyIdToHex(getMasterKeyId()) }, 0); /* // Remove all non-verifying self certificates @@ -218,6 +219,8 @@ public class UncachedKeyRing { }*/ + log.add(LogLevel.OK, LogType.MSG_KC_SUCCESS, null, 0); + return this; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java index 90d772bd1..9932e3e18 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java @@ -432,8 +432,9 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O toast.setText(str); toast.setDuration(duration); toast.setIndeterminate(duration == 0); - toast.setButtonText("View log"); toast.setSwipeToDismiss(true); + toast.setButtonIcon(R.drawable.ic_action_view_as_list, + getResources().getString(R.string.view_log)); toast.setOnClickWrapper(new OnClickWrapper("supercardtoast", new SuperToast.OnClickListener() { @Override 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 36d3e932e..5e038d3f6 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java @@ -54,7 +54,7 @@ public class LogDisplayFragment extends ListFragment { super(context, R.layout.log_display_item, log); mInflater = LayoutInflater.from(getContext()); dipFactor = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, - (float) 6, getResources().getDisplayMetrics()); + (float) 10, getResources().getDisplayMetrics()); } -- cgit v1.2.3 From e41e6ea0deec06703c5b9c80e429aff8ab110534 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 10 Jun 2014 15:27:26 +0200 Subject: import-log: more interface work --- .../keychain/pgp/PgpImportExport.java | 2 - .../keychain/provider/ProviderHelper.java | 44 +++++++++++++++------- .../keychain/service/OperationResultParcel.java | 26 ++++++++++--- .../keychain/service/OperationResults.java | 2 - .../keychain/ui/ImportKeysActivity.java | 36 +++++++++--------- .../keychain/ui/LogDisplayFragment.java | 1 + 6 files changed, 71 insertions(+), 40 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java index ebc5a7868..4cb41708d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java @@ -171,8 +171,6 @@ public class PgpImportExport { if (newKeys > 0 || oldKeys > 0) { if (badKeys > 0) { resultType = ImportResult.RESULT_PARTIAL_WITH_ERRORS; - } else if (log.containsWarnings()) { - resultType = ImportResult.RESULT_OK_WITH_WARNINGS; } else if (newKeys > 0 && oldKeys > 0) { resultType = ImportResult.RESULT_OK_BOTHKEYS; } else if (newKeys > 0) { 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 ab4672a98..cbaf72270 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -49,7 +49,6 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; import org.sufficientlysecure.keychain.remote.AccountSettings; import org.sufficientlysecure.keychain.remote.AppSettings; -import org.sufficientlysecure.keychain.service.OperationResults; import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.Log; @@ -252,7 +251,7 @@ public class ProviderHelper { throw new NotFoundException("Secret key not available!"); } return secret - ? new WrappedSecretKeyRing(blob, hasAnySecret, verified) + ? new WrappedSecretKeyRing(blob, true, verified) : new WrappedPublicKeyRing(blob, hasAnySecret, verified); } else { throw new NotFoundException("Key not found!"); @@ -274,14 +273,15 @@ public class ProviderHelper { return new OperationResultParcel(1, mLog); } - // Canonicalize this key, to assert a number of assumptions made about the key. + long masterKeyId = keyRing.getMasterKeyId(); + log(LogLevel.START, LogType.MSG_IP, + new String[]{ PgpKeyHelper.convertKeyIdToHex(masterKeyId) }); + mIndent += 1; + + // Canonicalize this key, to assert a number of assumptions made about it. keyRing = keyRing.canonicalize(mLog); UncachedPublicKey masterKey = keyRing.getPublicKey(); - long masterKeyId = masterKey.getKeyId(); - log(LogLevel.INFO, LogType.MSG_IP_IMPORTING, - new String[]{Long.toString(masterKeyId)}); - mIndent += 1; // IF there is a secret key, preserve it! UncachedKeyRing secretRing; @@ -342,12 +342,28 @@ public class ProviderHelper { values.put(Keys.ALGORITHM, key.getAlgorithm()); values.put(Keys.FINGERPRINT, key.getFingerprint()); - boolean c = key.canCertify(), s = key.canSign(), e = key.canEncrypt(); + boolean c = key.canCertify(), e = key.canEncrypt(), s = key.canSign(); values.put(Keys.CAN_CERTIFY, c); - values.put(Keys.CAN_SIGN, s); values.put(Keys.CAN_ENCRYPT, e); + values.put(Keys.CAN_SIGN, s); values.put(Keys.IS_REVOKED, key.isRevoked()); - log(LogLevel.DEBUG, LogType.MSG_IP_SUBKEY_FLAGS, new String[] { "X" }); + if (c) { + if (e) { + log(LogLevel.DEBUG,s ? LogType.MSG_IP_SUBKEY_FLAGS_CES + : LogType.MSG_IP_SUBKEY_FLAGS_CEX, null); + } else { + log(LogLevel.DEBUG, s ? LogType.MSG_IP_SUBKEY_FLAGS_CXS + : LogType.MSG_IP_SUBKEY_FLAGS_CXX, null); + } + } else { + if (e) { + log(LogLevel.DEBUG, s ? LogType.MSG_IP_SUBKEY_FLAGS_XES + : LogType.MSG_IP_SUBKEY_FLAGS_XEX, null); + } else { + log(LogLevel.DEBUG, s ? LogType.MSG_IP_SUBKEY_FLAGS_XXS + : LogType.MSG_IP_SUBKEY_FLAGS_XXX, null); + } + } Date creation = key.getCreationTime(); values.put(Keys.CREATION, creation.getTime() / 1000); @@ -531,7 +547,7 @@ public class ProviderHelper { mIndent -= 1; } - log(LogLevel.INFO, LogType.MSG_IP_SUCCESS); + log(LogLevel.OK, LogType.MSG_IP_SUCCESS); mIndent -= 1; return new OperationResultParcel(0, mLog); @@ -569,8 +585,8 @@ public class ProviderHelper { } long masterKeyId = keyRing.getMasterKeyId(); - log(LogLevel.INFO, LogType.MSG_IS_IMPORTING, - new String[]{Long.toString(masterKeyId)}); + log(LogLevel.START, LogType.MSG_IS, + new String[]{PgpKeyHelper.convertKeyIdToHex(masterKeyId)}); // save secret keyring try { @@ -626,7 +642,7 @@ public class ProviderHelper { // with has_secret = 0 } - log(LogLevel.INFO, LogType.MSG_IS_SUCCESS); + log(LogLevel.OK, LogType.MSG_IS_SUCCESS); return new OperationResultParcel(0, mLog); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index a1a1d0067..85b7c456f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -101,6 +101,9 @@ public class OperationResultParcel implements Parcelable { } public static enum LogType { + + // import public + MSG_IP(R.string.msg_ip), MSG_IP_APPLY_BATCH (R.string.msg_ip_apply_batch), MSG_IP_BAD_TYPE_SECRET (R.string.msg_ip_bad_type_secret), MSG_IP_DELETE_OLD_FAIL (R.string.msg_ip_delete_old_fail), @@ -109,7 +112,6 @@ public class OperationResultParcel implements Parcelable { MSG_IP_FAIL_IO_EXC (R.string.msg_ip_fail_io_exc), MSG_IP_FAIL_OP_EX (R.string.msg_ip_fail_op_ex), MSG_IP_FAIL_REMOTE_EX (R.string.msg_ip_fail_remote_ex), - MSG_IP_IMPORTING (R.string.msg_ip_importing), MSG_IP_INSERT_KEYRING (R.string.msg_ip_insert_keyring), MSG_IP_INSERT_SUBKEYS (R.string.msg_ip_insert_subkeys), MSG_IP_PRESERVING_SECRET (R.string.msg_ip_preserving_secret), @@ -118,6 +120,14 @@ public class OperationResultParcel implements Parcelable { MSG_IP_SUBKEY_EXPIRED (R.string.msg_ip_subkey_expired), MSG_IP_SUBKEY_EXPIRES (R.string.msg_ip_subkey_expires), MSG_IP_SUBKEY_FLAGS (R.string.msg_ip_subkey_flags), + MSG_IP_SUBKEY_FLAGS_CES (R.string.msg_ip_subkey_flags_ces), + MSG_IP_SUBKEY_FLAGS_CEX (R.string.msg_ip_subkey_flags_cex), + MSG_IP_SUBKEY_FLAGS_CXS (R.string.msg_ip_subkey_flags_cxs), + MSG_IP_SUBKEY_FLAGS_XES (R.string.msg_ip_subkey_flags_xes), + MSG_IP_SUBKEY_FLAGS_CXX (R.string.msg_ip_subkey_flags_cxx), + MSG_IP_SUBKEY_FLAGS_XEX (R.string.msg_ip_subkey_flags_xex), + MSG_IP_SUBKEY_FLAGS_XXS (R.string.msg_ip_subkey_flags_xxs), + MSG_IP_SUBKEY_FLAGS_XXX (R.string.msg_ip_subkey_flags_xxx), MSG_IP_SUBKEY_FUTURE (R.string.msg_ip_subkey_future), MSG_IP_SUCCESS (R.string.msg_ip_success), MSG_IP_TRUST_RETRIEVE (R.string.msg_ip_trust_retrieve), @@ -135,14 +145,20 @@ public class OperationResultParcel implements Parcelable { MSG_IP_UID_SELF_GOOD (R.string.msg_ip_uid_self_good), MSG_IP_UID_SELF_IGNORING_OLD (R.string.msg_ip_uid_self_ignoring_old), MSG_IP_UID_SELF_NEWER (R.string.msg_ip_uid_self_newer), + + // import secret + MSG_IS(R.string.msg_is), MSG_IS_BAD_TYPE_PUBLIC (R.string.msg_is_bad_type_public), - MSG_IS_IMPORTING (R.string.msg_is_importing), MSG_IS_IMPORTING_SUBKEYS (R.string.msg_is_importing_subkeys), MSG_IS_IO_EXCPTION (R.string.msg_is_io_excption), MSG_IS_SUBKEY_NONEXISTENT (R.string.msg_is_subkey_nonexistent), MSG_IS_SUBKEY_OK (R.string.msg_is_subkey_ok), MSG_IS_SUBKEY_STRIPPED (R.string.msg_is_subkey_stripped), MSG_IS_SUCCESS (R.string.msg_is_success), + + // keyring canonicalization + MSG_KC(R.string.msg_kc), + MSG_KC_SUCCESS(R.string.msg_kc_success), ; private final int mMsgId; @@ -156,12 +172,12 @@ public class OperationResultParcel implements Parcelable { /** Enumeration of possible log levels. */ public static enum LogLevel { - OK, + START, // should occur once at the start of each independent operation + OK, // should occur once at the end of a successful operation + ERROR, // should occur once at the end of a failed operation DEBUG, INFO, WARN, - /** If any ERROR log entry is included in the result, the overall operation should have failed. */ - ERROR, } @Override diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java index e08b50500..bee6917be 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java @@ -14,8 +14,6 @@ public abstract class OperationResults { public static final int RESULT_OK_BOTHKEYS = 2; // Operation ok, no new keys but upated ones (no warnings) public static final int RESULT_OK_UPDATED = 3; - // Operation ok, but with warnings - public static final int RESULT_OK_WITH_WARNINGS = 4; // Operation partially ok, but at least one key failed! public static final int RESULT_PARTIAL_WITH_ERRORS = 50; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java index 9932e3e18..61d7e7949 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java @@ -378,37 +378,37 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O final ImportResult result = returnData.getParcelable(KeychainIntentService.RESULT); - // , make pessimistic assumptions - String str = Integer.toString(result.getResult()); - int duration = 0, color = Style.RED; + String str = ""; + boolean hasWarnings = result.getLog().containsWarnings(); + int duration = 0, color = hasWarnings ? Style.ORANGE : Style.GREEN; + String withWarnings = hasWarnings + ? getResources().getString(R.string.with_warnings) : ""; switch(result.getResult()) { case ImportResult.RESULT_OK_NEWKEYS: - color = Style.GREEN; - duration = SuperToast.Duration.LONG; + if (!hasWarnings) { + duration = SuperToast.Duration.LONG; + } str = getResources().getQuantityString( - R.plurals.keys_added, result.mNewKeys, result.mNewKeys); + R.plurals.keys_added, result.mNewKeys, result.mNewKeys, withWarnings); break; case ImportResult.RESULT_OK_UPDATED: - color = Style.GREEN; - duration = SuperToast.Duration.LONG; + if (!hasWarnings) { + duration = SuperToast.Duration.LONG; + } str = getResources().getQuantityString( - R.plurals.keys_updated, result.mNewKeys, result.mNewKeys); + R.plurals.keys_updated, result.mNewKeys, result.mNewKeys, withWarnings); break; case ImportResult.RESULT_OK_BOTHKEYS: - color = Style.GREEN; - duration = SuperToast.Duration.LONG; + if (!hasWarnings) { + duration = SuperToast.Duration.LONG; + } str = getResources().getQuantityString( R.plurals.keys_added_and_updated_1, result.mNewKeys, result.mNewKeys); str += getResources().getQuantityString( - R.plurals.keys_added_and_updated_2, result.mUpdatedKeys, result.mUpdatedKeys); - break; - - case ImportResult.RESULT_OK_WITH_WARNINGS: - str = "ok with warnings"; - color = Style.ORANGE; + R.plurals.keys_added_and_updated_2, result.mUpdatedKeys, result.mUpdatedKeys, withWarnings); break; case ImportResult.RESULT_PARTIAL_WITH_ERRORS: @@ -435,6 +435,8 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O toast.setSwipeToDismiss(true); toast.setButtonIcon(R.drawable.ic_action_view_as_list, getResources().getString(R.string.view_log)); + toast.setButtonTextColor(R.color.emphasis_dark); + toast.setTextColor(R.color.emphasis_dark); toast.setOnClickWrapper(new OnClickWrapper("supercardtoast", new SuperToast.OnClickListener() { @Override 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 5e038d3f6..275c9ac18 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java @@ -73,6 +73,7 @@ public class LogDisplayFragment extends ListFragment { text.setPadding(entry.mIndent*dipFactor, 0, 0, 0); text.setText(getResources().getString(entry.mType.getMsgId(), (Object[]) entry.mParameters)); switch (entry.mLevel) { + case OK: text.setTextColor(Color.GREEN); break; case DEBUG: text.setTextColor(Color.GRAY); break; case INFO: text.setTextColor(Color.BLACK); break; case WARN: text.setTextColor(Color.YELLOW); break; -- cgit v1.2.3 From f38556cab1215f1e49f2e6c2e90627a4c00b02d5 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 10 Jun 2014 16:24:04 +0200 Subject: import-log: switch to flags instead of statuses for result int --- .../keychain/pgp/PgpImportExport.java | 44 ++++++----- .../keychain/provider/ProviderHelper.java | 24 +++--- .../keychain/service/OperationResultParcel.java | 9 ++- .../keychain/service/OperationResults.java | 55 ++++++++++--- .../keychain/ui/ImportKeysActivity.java | 91 +++++++++++----------- 5 files changed, 133 insertions(+), 90 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java index 4cb41708d..bafb086d0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java @@ -33,9 +33,9 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.service.KeychainIntentService; -import org.sufficientlysecure.keychain.service.OperationResultParcel; import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog; import org.sufficientlysecure.keychain.service.OperationResults.ImportResult; +import org.sufficientlysecure.keychain.service.OperationResults.SaveKeyringResult; import org.sufficientlysecure.keychain.util.Log; import java.io.ByteArrayOutputStream; @@ -152,9 +152,12 @@ public class PgpImportExport { } } - OperationResultParcel result = mProviderHelper.savePublicKeyRing(key); - - newKeys += 1; + SaveKeyringResult result = mProviderHelper.savePublicKeyRing(key); + if (result.updated()) { + newKeys += 1; + } else { + oldKeys += 1; + } } catch (PgpGeneralException e) { Log.e(Constants.TAG, "Encountered bad key on import!", e); @@ -166,23 +169,26 @@ public class PgpImportExport { } OperationLog log = mProviderHelper.getLog(); - int resultType; - // Any key imported - overall success - if (newKeys > 0 || oldKeys > 0) { + int resultType = 0; + // special return case: no new keys at all + if (badKeys == 0 && newKeys == 0 && oldKeys == 0) { + resultType = ImportResult.RESULT_FAIL_NOTHING; + } else { + if (newKeys > 0) { + resultType |= ImportResult.RESULT_OK_NEWKEYS; + } + if (oldKeys > 0) { + resultType |= ImportResult.RESULT_OK_UPDATED; + } if (badKeys > 0) { - resultType = ImportResult.RESULT_PARTIAL_WITH_ERRORS; - } else if (newKeys > 0 && oldKeys > 0) { - resultType = ImportResult.RESULT_OK_BOTHKEYS; - } else if (newKeys > 0) { - resultType = ImportResult.RESULT_OK_NEWKEYS; - } else { - resultType = ImportResult.RESULT_OK_UPDATED; + resultType |= ImportResult.RESULT_WITH_ERRORS; + if (newKeys == 0 && oldKeys == 0) { + resultType |= ImportResult.RESULT_ERROR; + } + } + if (log.containsWarnings()) { + resultType |= ImportResult.RESULT_WITH_WARNINGS; } - // No keys imported, overall failure - } else if (badKeys > 0) { - resultType = ImportResult.RESULT_FAIL_ERROR; - } else { - resultType = ImportResult.RESULT_FAIL_NOTHING; } return new ImportResult(resultType, log, newKeys, oldKeys, badKeys); 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 cbaf72270..00db473b3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -49,6 +49,7 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; import org.sufficientlysecure.keychain.remote.AccountSettings; import org.sufficientlysecure.keychain.remote.AppSettings; +import org.sufficientlysecure.keychain.service.OperationResults.SaveKeyringResult; import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.Log; @@ -267,12 +268,15 @@ public class ProviderHelper { * Saves PGPPublicKeyRing with its keys and userIds in DB */ @SuppressWarnings("unchecked") - public OperationResultParcel savePublicKeyRing(UncachedKeyRing keyRing) { + public SaveKeyringResult savePublicKeyRing(UncachedKeyRing keyRing) { if (keyRing.isSecret()) { log(LogLevel.ERROR, LogType.MSG_IP_BAD_TYPE_SECRET); - return new OperationResultParcel(1, mLog); + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); } + // start with ok result + int result = SaveKeyringResult.SAVED_PUBLIC; + long masterKeyId = keyRing.getMasterKeyId(); log(LogLevel.START, LogType.MSG_IP, new String[]{ PgpKeyHelper.convertKeyIdToHex(masterKeyId) }); @@ -296,6 +300,7 @@ public class ProviderHelper { try { mContentResolver.delete(KeyRingData.buildPublicKeyRingUri(Long.toString(masterKeyId)), null, null); log(LogLevel.DEBUG, LogType.MSG_IP_DELETE_OLD_OK); + result |= SaveKeyringResult.UPDATED; } catch (UnsupportedOperationException e) { Log.e(Constants.TAG, "Key could not be deleted! Maybe we are creating a new one!", e); log(LogLevel.DEBUG, LogType.MSG_IP_DELETE_OLD_FAIL); @@ -315,7 +320,7 @@ public class ProviderHelper { values.put(KeyRingData.KEY_RING_DATA, keyRing.getEncoded()); } catch (IOException e) { log(LogLevel.ERROR, LogType.MSG_IP_ENCODE_FAIL); - return new OperationResultParcel(1, mLog); + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); } Uri uri = KeyRingData.buildPublicKeyRingUri(Long.toString(masterKeyId)); @@ -371,7 +376,7 @@ public class ProviderHelper { log(LogLevel.ERROR, LogType.MSG_IP_SUBKEY_FUTURE, new String[] { creation.toString() }); - return new OperationResultParcel(1, mLog); + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); } Date expiryDate = key.getExpiryTime(); if (expiryDate != null) { @@ -436,7 +441,7 @@ public class ProviderHelper { if (!cert.verifySignature(masterKey, userId)) { // Bad self certification? That's kinda bad... log(LogLevel.ERROR, LogType.MSG_IP_UID_SELF_BAD); - return new OperationResultParcel(1, mLog); + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); } // if we already have a cert.. @@ -526,17 +531,17 @@ public class ProviderHelper { log(LogLevel.ERROR, LogType.MSG_IP_FAIL_IO_EXC); Log.e(Constants.TAG, "IOException during import", e); mIndent -= 1; - return new OperationResultParcel(1, mLog); + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); } catch (RemoteException e) { log(LogLevel.ERROR, LogType.MSG_IP_FAIL_REMOTE_EX); Log.e(Constants.TAG, "RemoteException during import", e); mIndent -= 1; - return new OperationResultParcel(1, mLog); + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); } catch (OperationApplicationException e) { log(LogLevel.ERROR, LogType.MSG_IP_FAIL_OP_EX); Log.e(Constants.TAG, "OperationApplicationException during import", e); mIndent -= 1; - return new OperationResultParcel(1, mLog); + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); } // Save the saved keyring (if any) @@ -544,12 +549,13 @@ public class ProviderHelper { log(LogLevel.DEBUG, LogType.MSG_IP_REINSERT_SECRET); mIndent += 1; saveSecretKeyRing(secretRing); + result |= SaveKeyringResult.SAVED_SECRET; mIndent -= 1; } log(LogLevel.OK, LogType.MSG_IP_SUCCESS); mIndent -= 1; - return new OperationResultParcel(0, mLog); + return new SaveKeyringResult(result, mLog); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index 85b7c456f..8a3b06f67 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -18,12 +18,13 @@ import java.util.ArrayList; * */ public class OperationResultParcel implements Parcelable { - /** Holds the overall result, the number specifying varying degrees of success. - * Values smaller than 100 are considered an overall success. */ + /** Holds the overall result, the number specifying varying degrees of success. The first bit + * is 0 on overall success, 1 on overall failure. All other bits may be used for more specific + * conditions. */ final int mResult; public static final int RESULT_OK = 0; - public static final int RESULT_ERROR = 100; + public static final int RESULT_ERROR = 1; /// A list of log entries tied to the operation result. final OperationLog mLog; @@ -44,7 +45,7 @@ public class OperationResultParcel implements Parcelable { } public boolean isSuccessful() { - return mResult < 100; + return (mResult & 1) == 1; } public OperationLog getLog() { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java index bee6917be..342d07953 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java @@ -8,20 +8,31 @@ public abstract class OperationResults { public final int mNewKeys, mUpdatedKeys, mBadKeys; - // Operation ok, at least one new key (no warnings) - public static final int RESULT_OK_NEWKEYS = 1; - // Operation ok, at least one new and one updated key (no warnings) - public static final int RESULT_OK_BOTHKEYS = 2; - // Operation ok, no new keys but upated ones (no warnings) - public static final int RESULT_OK_UPDATED = 3; + // At least one new key + public static final int RESULT_OK_NEWKEYS = 2; + // At least one updated key + public static final int RESULT_OK_UPDATED = 4; + // At least one key failed (might still be an overall success) + public static final int RESULT_WITH_ERRORS = 8; + // There are warnings in the log + public static final int RESULT_WITH_WARNINGS = 16; - // Operation partially ok, but at least one key failed! - public static final int RESULT_PARTIAL_WITH_ERRORS = 50; + // No keys to import... + public static final int RESULT_FAIL_NOTHING = 32 +1; - // Operation failed, errors thrown and no new keys imported - public static final int RESULT_FAIL_ERROR = 100; - // Operation failed, no keys to import... - public static final int RESULT_FAIL_NOTHING = 101; + public boolean isOkBoth() { + return (mResult & (RESULT_OK_NEWKEYS | RESULT_OK_UPDATED)) + == (RESULT_OK_NEWKEYS | RESULT_OK_UPDATED); + } + public boolean isOkNew() { + return (mResult & RESULT_OK_NEWKEYS) > 0; + } + public boolean isOkUpdated() { + return (mResult & RESULT_OK_UPDATED) > 0; + } + public boolean isFailNothing() { + return (mResult & RESULT_FAIL_NOTHING) > 0; + } public ImportResult(Parcel source) { super(source); @@ -58,4 +69,24 @@ public abstract class OperationResults { } + public static class SaveKeyringResult extends OperationResultParcel { + + public SaveKeyringResult(int result, OperationLog log) { + super(result, log); + } + + // Some old key was updated + public static final int UPDATED = 2; + + // Public key was saved + public static final int SAVED_PUBLIC = 8; + // Secret key was saved (not exclusive with public!) + public static final int SAVED_SECRET = 16; + + public boolean updated() { + return (mResult & UPDATED) == UPDATED; + } + + } + } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java index 61d7e7949..3ff4f9887 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java @@ -378,55 +378,54 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O final ImportResult result = returnData.getParcelable(KeychainIntentService.RESULT); - String str = ""; - boolean hasWarnings = result.getLog().containsWarnings(); - int duration = 0, color = hasWarnings ? Style.ORANGE : Style.GREEN; - String withWarnings = hasWarnings - ? getResources().getString(R.string.with_warnings) : ""; - - switch(result.getResult()) { - case ImportResult.RESULT_OK_NEWKEYS: - if (!hasWarnings) { - duration = SuperToast.Duration.LONG; - } - str = getResources().getQuantityString( - R.plurals.keys_added, result.mNewKeys, result.mNewKeys, withWarnings); - break; + int resultType = result.getResult(); - case ImportResult.RESULT_OK_UPDATED: - if (!hasWarnings) { - duration = SuperToast.Duration.LONG; - } - str = getResources().getQuantityString( - R.plurals.keys_updated, result.mNewKeys, result.mNewKeys, withWarnings); - break; + String str; + int duration, color; - case ImportResult.RESULT_OK_BOTHKEYS: - if (!hasWarnings) { - duration = SuperToast.Duration.LONG; - } - str = getResources().getQuantityString( - R.plurals.keys_added_and_updated_1, result.mNewKeys, result.mNewKeys); - str += getResources().getQuantityString( - R.plurals.keys_added_and_updated_2, result.mUpdatedKeys, result.mUpdatedKeys, withWarnings); - break; + // Not an overall failure + if ((resultType & ImportResult.RESULT_ERROR) == 0) { + String withWarnings; - case ImportResult.RESULT_PARTIAL_WITH_ERRORS: - str = "partial with errors"; + // Any warnings? + if ((resultType & ImportResult.RESULT_WITH_WARNINGS) > 0) { + duration = 0; color = Style.ORANGE; - break; - - case ImportResult.RESULT_FAIL_ERROR: - str = "fail error"; - color = Style.RED; - break; - - case ImportResult.RESULT_FAIL_NOTHING: - str = getString(R.string.no_keys_added_or_updated); + withWarnings = getResources().getString(R.string.import_with_warnings); + } else { + duration = SuperToast.Duration.LONG; + color = Style.GREEN; + withWarnings = ""; + } + + // New and updated keys + if (result.isOkBoth()) { + str = getResources().getQuantityString( + R.plurals.import_keys_added_and_updated_1, result.mNewKeys, result.mNewKeys); + str += getResources().getQuantityString( + R.plurals.import_keys_added_and_updated_2, result.mUpdatedKeys, result.mUpdatedKeys, withWarnings); + } else if (result.isOkNew()) { + str = getResources().getQuantityString( + R.plurals.import_keys_added, result.mNewKeys, result.mNewKeys, withWarnings); + } else if (result.isOkUpdated()) { + str = getResources().getQuantityString( + R.plurals.import_keys_updated, result.mUpdatedKeys, result.mUpdatedKeys, withWarnings); + } else { + duration = 0; color = Style.RED; - break; - + str = "internal error"; + } + + } else { + duration = 0; + color = Style.RED; + if (result.isFailNothing()) { + str = getString(R.string.import_error_nothing); + } else { + str = getString(R.string.import_error); + } } + SuperCardToast toast = new SuperCardToast(ImportKeysActivity.this, SuperToast.Type.BUTTON, Style.getStyle(color, SuperToast.Animations.POPUP)); toast.setText(str); @@ -434,9 +433,9 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O toast.setIndeterminate(duration == 0); toast.setSwipeToDismiss(true); toast.setButtonIcon(R.drawable.ic_action_view_as_list, - getResources().getString(R.string.view_log)); - toast.setButtonTextColor(R.color.emphasis_dark); - toast.setTextColor(R.color.emphasis_dark); + getResources().getString(R.string.import_view_log)); + toast.setButtonTextColor(getResources().getColor(R.color.black)); + toast.setTextColor(getResources().getColor(R.color.black)); toast.setOnClickWrapper(new OnClickWrapper("supercardtoast", new SuperToast.OnClickListener() { @Override -- cgit v1.2.3 From 3895c10a5821a3ce11946c4cd88298c0b7410d5f Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 10 Jun 2014 20:06:28 +0200 Subject: import-log: work on log fragment ui --- .../keychain/provider/ProviderHelper.java | 2 +- .../keychain/service/OperationResultParcel.java | 6 +- .../keychain/ui/LogDisplayActivity.java | 4 + .../keychain/ui/LogDisplayFragment.java | 108 ++++++++++++++++++--- 4 files changed, 102 insertions(+), 18 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') 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 00db473b3..f83ea24df 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -553,8 +553,8 @@ public class ProviderHelper { mIndent -= 1; } - log(LogLevel.OK, LogType.MSG_IP_SUCCESS); mIndent -= 1; + log(LogLevel.OK, LogType.MSG_IP_SUCCESS); return new SaveKeyringResult(result, mLog); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index 8a3b06f67..4c288502c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -173,12 +173,12 @@ public class OperationResultParcel implements Parcelable { /** Enumeration of possible log levels. */ public static enum LogLevel { - START, // should occur once at the start of each independent operation - OK, // should occur once at the end of a successful operation - ERROR, // should occur once at the end of a failed operation DEBUG, INFO, WARN, + ERROR, // should occur once at the end of a failed operation + START, // should occur once at the start of each independent operation + OK, // should occur once at the end of a successful operation } @Override diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java index 28c120d01..a0d449195 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java @@ -1,7 +1,11 @@ package org.sufficientlysecure.keychain.ui; import android.os.Bundle; +import android.support.v4.view.GestureDetectorCompat; import android.support.v7.app.ActionBarActivity; +import android.view.GestureDetector; +import android.view.GestureDetector.SimpleOnGestureListener; +import android.view.MotionEvent; import org.sufficientlysecure.keychain.R; 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 275c9ac18..980ee5e4a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java @@ -5,22 +5,40 @@ import android.content.Intent; import android.graphics.Color; import android.os.Bundle; import android.support.v4.app.ListFragment; +import android.support.v4.view.GestureDetectorCompat; +import android.support.v4.view.MotionEventCompat; import android.util.TypedValue; +import android.view.GestureDetector; +import android.view.GestureDetector.SimpleOnGestureListener; import android.view.LayoutInflater; +import android.view.MotionEvent; import android.view.View; +import android.view.View.OnTouchListener; import android.view.ViewGroup; import android.widget.ArrayAdapter; +import android.widget.Filterable; +import android.widget.ImageView; import android.widget.TextView; +import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.service.OperationResultParcel; import org.sufficientlysecure.keychain.service.OperationResultParcel.LogEntryParcel; +import org.sufficientlysecure.keychain.service.OperationResultParcel.LogLevel; +import org.sufficientlysecure.keychain.util.Log; import java.util.ArrayList; +import java.util.HashMap; -public class LogDisplayFragment extends ListFragment { +public class LogDisplayFragment extends ListFragment implements OnTouchListener { + HashMap mAdapters = new HashMap(); LogAdapter mAdapter; + LogLevel mLevel = LogLevel.DEBUG; + + OperationResultParcel mResult; + + GestureDetector mDetector; public static final String EXTRA_RESULT = "log"; @@ -34,15 +52,68 @@ public class LogDisplayFragment extends ListFragment { return; } - OperationResultParcel result = intent.getParcelableExtra(EXTRA_RESULT); - if (result == null) { + mResult = intent.getParcelableExtra(EXTRA_RESULT); + if (mResult == null) { getActivity().finish(); return; } - mAdapter = new LogAdapter(getActivity(), result.getLog()); + mAdapter = new LogAdapter(getActivity(), mResult.getLog(), LogLevel.DEBUG); + mAdapters.put(LogLevel.DEBUG, mAdapter); setListAdapter(mAdapter); + mDetector = new GestureDetector(getActivity(), new SimpleOnGestureListener() { + @Override + public boolean onFling(MotionEvent e1, MotionEvent e2, float vx, float vy) { + Log.d(Constants.TAG, "x: " + vx + ", y: " + vy); + if (vx < -2000) { + decreaseLogLevel(); + } else if (vx > 2000) { + increaseLogLevel(); + } + return true; + } + }); + + } + + public void decreaseLogLevel() { + switch (mLevel) { + case DEBUG: mLevel = LogLevel.INFO; break; + case INFO: mLevel = LogLevel.WARN; break; + } + refreshLevel(); + } + + public void increaseLogLevel() { + switch (mLevel) { + case INFO: mLevel = LogLevel.DEBUG; break; + case WARN: mLevel = LogLevel.INFO; break; + } + refreshLevel(); + } + + private void refreshLevel() { + /* TODO not sure if this is a good idea + if (!mAdapters.containsKey(mLevel)) { + mAdapters.put(mLevel, new LogAdapter(getActivity(), mResult.getLog(), mLevel)); + } + mAdapter = mAdapters.get(mLevel); + setListAdapter(mAdapter); + */ + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + getListView().setDividerHeight(0); + getListView().setOnTouchListener(this); + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + mDetector.onTouchEvent(event); + return false; } private class LogAdapter extends ArrayAdapter { @@ -50,12 +121,18 @@ public class LogDisplayFragment extends ListFragment { private LayoutInflater mInflater; private int dipFactor; - public LogAdapter(Context context, ArrayList log) { - super(context, R.layout.log_display_item, log); + public LogAdapter(Context context, ArrayList log, LogLevel level) { + super(context, R.layout.log_display_item); mInflater = LayoutInflater.from(getContext()); dipFactor = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, - (float) 10, getResources().getDisplayMetrics()); - + (float) 8, getResources().getDisplayMetrics()); + // we can't use addAll for a LogLevel.DEBUG shortcut here, unfortunately :( + for (LogEntryParcel e : log) { + if (e.mLevel.ordinal() >= level.ordinal()) { + add(e); + } + } + notifyDataSetChanged(); } @Override @@ -69,15 +146,18 @@ public class LogDisplayFragment extends ListFragment { } else { text = (TextView) convertView.getTag(); } + ImageView img = (ImageView) convertView.findViewById(R.id.log_img); - text.setPadding(entry.mIndent*dipFactor, 0, 0, 0); text.setText(getResources().getString(entry.mType.getMsgId(), (Object[]) entry.mParameters)); + text.setTextColor(entry.mLevel == LogLevel.DEBUG ? Color.GRAY : Color.BLACK); + convertView.setPadding((entry.mIndent) * dipFactor, 0, 0, 0); switch (entry.mLevel) { - case OK: text.setTextColor(Color.GREEN); break; - case DEBUG: text.setTextColor(Color.GRAY); break; - case INFO: text.setTextColor(Color.BLACK); break; - case WARN: text.setTextColor(Color.YELLOW); break; - case ERROR: text.setTextColor(Color.RED); break; + case DEBUG: img.setBackgroundColor(Color.GRAY); break; + case INFO: img.setBackgroundColor(Color.BLACK); break; + case WARN: img.setBackgroundColor(Color.YELLOW); break; + case ERROR: img.setBackgroundColor(Color.RED); break; + case START: img.setBackgroundColor(Color.GREEN); break; + case OK: img.setBackgroundColor(Color.GREEN); break; } return convertView; -- cgit v1.2.3 From 8d75d3e00e02527c4bc717cbcd937b1457eb4bda Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Wed, 11 Jun 2014 00:37:23 +0200 Subject: import-log: use holder pattern in log fragment --- .../keychain/ui/LogDisplayFragment.java | 40 +++++++++++++--------- 1 file changed, 24 insertions(+), 16 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') 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 980ee5e4a..496e98c18 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java @@ -5,8 +5,6 @@ import android.content.Intent; import android.graphics.Color; import android.os.Bundle; import android.support.v4.app.ListFragment; -import android.support.v4.view.GestureDetectorCompat; -import android.support.v4.view.MotionEventCompat; import android.util.TypedValue; import android.view.GestureDetector; import android.view.GestureDetector.SimpleOnGestureListener; @@ -16,7 +14,6 @@ import android.view.View; import android.view.View.OnTouchListener; import android.view.ViewGroup; import android.widget.ArrayAdapter; -import android.widget.Filterable; import android.widget.ImageView; import android.widget.TextView; @@ -135,29 +132,40 @@ public class LogDisplayFragment extends ListFragment implements OnTouchListener notifyDataSetChanged(); } + private class ItemHolder { + final TextView mText; + final ImageView mImg; + public ItemHolder(TextView text, ImageView image) { + mText = text; + mImg = image; + } + } + @Override public View getView(int position, View convertView, ViewGroup parent) { LogEntryParcel entry = getItem(position); - TextView text; + ItemHolder ih; if (convertView == null) { convertView = mInflater.inflate(R.layout.log_display_item, parent, false); - text = (TextView) convertView.findViewById(R.id.log_text); - convertView.setTag(text); + ih = new ItemHolder( + (TextView) convertView.findViewById(R.id.log_text), + (ImageView) convertView.findViewById(R.id.log_img) + ); + convertView.setTag(ih); } else { - text = (TextView) convertView.getTag(); + ih = (ItemHolder) convertView.getTag(); } - ImageView img = (ImageView) convertView.findViewById(R.id.log_img); - text.setText(getResources().getString(entry.mType.getMsgId(), (Object[]) entry.mParameters)); - text.setTextColor(entry.mLevel == LogLevel.DEBUG ? Color.GRAY : Color.BLACK); + ih.mText.setText(getResources().getString(entry.mType.getMsgId(), (Object[]) entry.mParameters)); + ih.mText.setTextColor(entry.mLevel == LogLevel.DEBUG ? Color.GRAY : Color.BLACK); convertView.setPadding((entry.mIndent) * dipFactor, 0, 0, 0); switch (entry.mLevel) { - case DEBUG: img.setBackgroundColor(Color.GRAY); break; - case INFO: img.setBackgroundColor(Color.BLACK); break; - case WARN: img.setBackgroundColor(Color.YELLOW); break; - case ERROR: img.setBackgroundColor(Color.RED); break; - case START: img.setBackgroundColor(Color.GREEN); break; - case OK: img.setBackgroundColor(Color.GREEN); break; + case DEBUG: ih.mImg.setBackgroundColor(Color.GRAY); break; + case INFO: ih.mImg.setBackgroundColor(Color.BLACK); break; + case WARN: ih.mImg.setBackgroundColor(Color.YELLOW); break; + case ERROR: ih.mImg.setBackgroundColor(Color.RED); break; + case START: ih.mImg.setBackgroundColor(Color.GREEN); break; + case OK: ih.mImg.setBackgroundColor(Color.GREEN); break; } return convertView; -- cgit v1.2.3 From 47368f1d24cb3b4112133a6dddd2793e6787dfdd Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 12 Jun 2014 01:37:49 +0200 Subject: import-log: better way to check self trust, and fix log level filtering --- .../keychain/provider/ProviderHelper.java | 18 +++++------------- .../keychain/service/OperationResultParcel.java | 4 +--- 2 files changed, 6 insertions(+), 16 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') 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 f83ea24df..8d95da8b0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -399,21 +399,12 @@ public class ProviderHelper { } mIndent -= 1; - log(LogLevel.DEBUG, LogType.MSG_IP_TRUST_RETRIEVE); // get a list of owned secret keys, for verification filtering LongSparseArray trustedKeys = getUncachedMasterKeys(KeyRingData.buildSecretKeyRingUri()); - // special case: available secret keys verify themselves! - if (secretRing != null) { - trustedKeys.put(secretRing.getMasterKeyId(), secretRing.getPublicKey()); - log(LogLevel.INFO, LogType.MSG_IP_TRUST_USING_SEC, new String[]{ - Integer.toString(trustedKeys.size()) - }); - } else { - log(LogLevel.INFO, LogType.MSG_IP_TRUST_USING, new String[] { - Integer.toString(trustedKeys.size()) - }); - } + log(LogLevel.INFO, LogType.MSG_IP_TRUST_USING, new String[] { + Integer.toString(trustedKeys.size()) + }); // classify and order user ids. primary are moved to the front, revoked to the back, // otherwise the order in the keyfile is preserved. @@ -513,7 +504,8 @@ public class ProviderHelper { // no self cert is bad, but allowed by the rfc... if (item.selfCert != null) { operations.add(buildCertOperations( - masterKeyId, userIdRank, item.selfCert, Certs.VERIFIED_SELF)); + masterKeyId, userIdRank, item.selfCert, + secretRing != null ? Certs.VERIFIED_SECRET : Certs.VERIFIED_SELF)); } // don't bother with trusted certs if the uid is revoked, anyways if (item.isRevoked) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index 4c288502c..473cf8244 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -133,7 +133,6 @@ public class OperationResultParcel implements Parcelable { MSG_IP_SUCCESS (R.string.msg_ip_success), MSG_IP_TRUST_RETRIEVE (R.string.msg_ip_trust_retrieve), MSG_IP_TRUST_USING (R.string.msg_ip_trust_using), - MSG_IP_TRUST_USING_SEC (R.string.msg_ip_trust_using_sec), MSG_IP_UID_CERT_BAD (R.string.msg_ip_uid_cert_bad), MSG_IP_UID_CERT_ERROR (R.string.msg_ip_uid_cert_error), MSG_IP_UID_CERT_GOOD (R.string.msg_ip_uid_cert_good), @@ -210,9 +209,8 @@ public class OperationResultParcel implements Parcelable { } public boolean containsWarnings() { - int warn = LogLevel.WARN.ordinal(); for(LogEntryParcel entry : new IterableIterator(iterator())) { - if (entry.mLevel.ordinal() >= warn) { + if (entry.mLevel == LogLevel.WARN || entry.mLevel == LogLevel.ERROR) { return true; } } -- cgit v1.2.3 From dea98a4a7e3143acfc01ce1567a9d17c25025b4d Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 12 Jun 2014 01:52:41 +0200 Subject: import-log: properly distinguish return states --- .../keychain/pgp/PgpImportExport.java | 17 +++++++---------- .../keychain/provider/ProviderHelper.java | 18 ++++++++++-------- .../keychain/service/OperationResultParcel.java | 4 ++-- .../keychain/service/OperationResults.java | 6 +++--- .../keychain/ui/ImportKeysActivity.java | 6 +++--- 5 files changed, 25 insertions(+), 26 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java index bafb086d0..bb45cc7db 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java @@ -58,10 +58,6 @@ public class PgpImportExport { private ProviderHelper mProviderHelper; - public static final int RETURN_OK = 0; - public static final int RETURN_BAD = -2; - public static final int RETURN_UPDATED = 1; - public PgpImportExport(Context context, Progressable progressable) { super(); this.mContext = context; @@ -118,10 +114,9 @@ public class PgpImportExport { if (aos != null) { aos.close(); } - if (bos != null) { - bos.close(); - } + bos.close(); } catch (IOException e) { + // this is just a finally thing, no matter if it doesn't work out. } } } @@ -153,10 +148,12 @@ public class PgpImportExport { } SaveKeyringResult result = mProviderHelper.savePublicKeyRing(key); - if (result.updated()) { - newKeys += 1; - } else { + if (!result.success()) { + badKeys += 1; + } else if (result.updated()) { oldKeys += 1; + } else { + newKeys += 1; } } catch (PgpGeneralException e) { 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 8d95da8b0..40af285e1 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -297,12 +297,12 @@ public class ProviderHelper { } // delete old version of this keyRing, which also deletes all keys and userIds on cascade - try { - mContentResolver.delete(KeyRingData.buildPublicKeyRingUri(Long.toString(masterKeyId)), null, null); + int deleted = mContentResolver.delete( + KeyRingData.buildPublicKeyRingUri(Long.toString(masterKeyId)), null, null); + if (deleted > 0) { log(LogLevel.DEBUG, LogType.MSG_IP_DELETE_OLD_OK); result |= SaveKeyringResult.UPDATED; - } catch (UnsupportedOperationException e) { - Log.e(Constants.TAG, "Key could not be deleted! Maybe we are creating a new one!", e); + } else { log(LogLevel.DEBUG, LogType.MSG_IP_DELETE_OLD_FAIL); } @@ -577,14 +577,16 @@ public class ProviderHelper { * is already in the database! */ public OperationResultParcel saveSecretKeyRing(UncachedKeyRing keyRing) { - if (!keyRing.isSecret()) { - log(LogLevel.ERROR, LogType.MSG_IS_BAD_TYPE_PUBLIC); - return new OperationResultParcel(1, mLog); - } long masterKeyId = keyRing.getMasterKeyId(); log(LogLevel.START, LogType.MSG_IS, new String[]{PgpKeyHelper.convertKeyIdToHex(masterKeyId)}); + mIndent += 1; + + if (!keyRing.isSecret()) { + log(LogLevel.ERROR, LogType.MSG_IS_BAD_TYPE_PUBLIC); + return new OperationResultParcel(1, mLog); + } // save secret keyring try { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index 473cf8244..67ff8318b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -44,8 +44,8 @@ public class OperationResultParcel implements Parcelable { return mResult; } - public boolean isSuccessful() { - return (mResult & 1) == 1; + public boolean success() { + return (mResult & 1) == 0; } public OperationLog getLog() { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java index 342d07953..6c44b01f1 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java @@ -25,13 +25,13 @@ public abstract class OperationResults { == (RESULT_OK_NEWKEYS | RESULT_OK_UPDATED); } public boolean isOkNew() { - return (mResult & RESULT_OK_NEWKEYS) > 0; + return (mResult & RESULT_OK_NEWKEYS) == RESULT_OK_NEWKEYS; } public boolean isOkUpdated() { - return (mResult & RESULT_OK_UPDATED) > 0; + return (mResult & RESULT_OK_UPDATED) == RESULT_OK_UPDATED; } public boolean isFailNothing() { - return (mResult & RESULT_FAIL_NOTHING) > 0; + return (mResult & RESULT_FAIL_NOTHING) == RESULT_FAIL_NOTHING; } public ImportResult(Parcel source) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java index 3ff4f9887..7fa18406c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java @@ -404,12 +404,12 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O R.plurals.import_keys_added_and_updated_1, result.mNewKeys, result.mNewKeys); str += getResources().getQuantityString( R.plurals.import_keys_added_and_updated_2, result.mUpdatedKeys, result.mUpdatedKeys, withWarnings); - } else if (result.isOkNew()) { - str = getResources().getQuantityString( - R.plurals.import_keys_added, result.mNewKeys, result.mNewKeys, withWarnings); } else if (result.isOkUpdated()) { str = getResources().getQuantityString( R.plurals.import_keys_updated, result.mUpdatedKeys, result.mUpdatedKeys, withWarnings); + } else if (result.isOkNew()) { + str = getResources().getQuantityString( + R.plurals.import_keys_added, result.mNewKeys, result.mNewKeys, withWarnings); } else { duration = 0; color = Style.RED; -- cgit v1.2.3 From 466eddb0051d8b19576b60be7e3769f13b308a57 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 12 Jun 2014 15:36:35 +0200 Subject: canonicalize: implementation, first draft --- .../keychain/pgp/UncachedKeyRing.java | 294 +++++++++++++++++++-- .../keychain/pgp/WrappedSignature.java | 26 +- .../keychain/provider/ProviderHelper.java | 2 +- .../keychain/service/OperationResultParcel.java | 23 +- 4 files changed, 317 insertions(+), 28 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java index 7853d0b00..215c17590 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java @@ -5,16 +5,17 @@ import org.spongycastle.bcpg.S2K; import org.spongycastle.openpgp.PGPKeyRing; import org.spongycastle.openpgp.PGPObjectFactory; import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyRing; import org.spongycastle.openpgp.PGPSecretKey; import org.spongycastle.openpgp.PGPSecretKeyRing; import org.spongycastle.openpgp.PGPSignature; import org.spongycastle.openpgp.PGPUtil; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; -import org.sufficientlysecure.keychain.service.OperationResultParcel; import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog; import org.sufficientlysecure.keychain.service.OperationResultParcel.LogLevel; import org.sufficientlysecure.keychain.service.OperationResultParcel.LogType; +import org.sufficientlysecure.keychain.service.OperationResults.SaveKeyringResult; import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.Log; @@ -179,51 +180,300 @@ public class UncachedKeyRing { * * More specifically: * - Remove all non-verifying self-certificates - * - Remove all expired self-certificates * - Remove all certificates flagged as "local" * - Remove all certificates which are superseded by a newer one on the same target * - * After this cleaning, a number of checks are done: + * After this cleaning, a number of checks are done: TODO implement * - See if each subkey retains a valid self certificate * - See if each user id retains a valid self certificate * * This operation writes an OperationLog which can be used as part of a OperationResultParcel. * - * If any of these checks fail, the operation as a whole fails and the keyring is declared - * unusable. (TODO: allow forcing of import?) - * - * TODO implement - * * @return A canonicalized key * */ - public UncachedKeyRing canonicalize(OperationLog log) { - if(isSecret()) { + public UncachedKeyRing canonicalize(OperationLog log, int indent) { + if (isSecret()) { throw new RuntimeException("Tried to canonicalize non-secret keyring. " + "This is a programming error and should never happen!"); } - // dummy log.add(LogLevel.START, LogType.MSG_KC, - new String[] { PgpKeyHelper.convertKeyIdToHex(getMasterKeyId()) }, 0); + new String[]{PgpKeyHelper.convertKeyIdToHex(getMasterKeyId())}, indent); + indent += 1; + + int removedCerts = 0; + + PGPPublicKeyRing ring = (PGPPublicKeyRing) mRing; + PGPPublicKey masterKey = mRing.getPublicKey(); + final long masterKeyId = masterKey.getKeyID(); + + { + log.add(LogLevel.DEBUG, LogType.MSG_KC_MASTER, + new String[]{PgpKeyHelper.convertKeyIdToHex(masterKey.getKeyID())}, indent); + indent += 1; + + PGPPublicKey modified = masterKey; + PGPSignature revocation = null; + for (PGPSignature zert : new IterableIterator(masterKey.getSignatures())) { + int type = zert.getSignatureType(); + // Disregard certifications on user ids, we will deal with those later + if (type == PGPSignature.NO_CERTIFICATION + || type == PGPSignature.DEFAULT_CERTIFICATION + || type == PGPSignature.CASUAL_CERTIFICATION + || type == PGPSignature.POSITIVE_CERTIFICATION + || type == PGPSignature.CERTIFICATION_REVOCATION) { + continue; + } + WrappedSignature cert = new WrappedSignature(zert); + + if (type != PGPSignature.KEY_REVOCATION) { + // Unknown type, just remove + log.add(LogLevel.WARN, LogType.MSG_KC_CERT_BAD_TYPE, new String[]{ + "0x" + Integer.toString(type, 16) + }, indent); + modified = PGPPublicKey.removeCertification(modified, zert); + removedCerts += 1; + continue; + } + + try { + cert.init(masterKey); + if (!cert.verifySignature(masterKey)) { + log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD, null, indent); + modified = PGPPublicKey.removeCertification(modified, zert); + removedCerts += 1; + continue; + } + } catch (PgpGeneralException e) { + log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD_ERR, null, indent); + modified = PGPPublicKey.removeCertification(modified, zert); + removedCerts += 1; + continue; + } + + // first revocation? fine then. + if (revocation == null) { + revocation = zert; + // more revocations? at least one is superfluous, then. + } else if (revocation.getCreationTime().before(zert.getCreationTime())) { + modified = PGPPublicKey.removeCertification(modified, revocation); + removedCerts += 1; + log.add(LogLevel.INFO, LogType.MSG_KC_REVOKE_DUP, null, indent); + revocation = zert; + } else { + modified = PGPPublicKey.removeCertification(modified, zert); + removedCerts += 1; + log.add(LogLevel.INFO, LogType.MSG_KC_REVOKE_DUP, null, indent); + } + } + + for (String userId : new IterableIterator(masterKey.getUserIDs())) { + PGPSignature selfCert = null; + revocation = null; + + // look through signatures for this specific key + for (PGPSignature zert : new IterableIterator( + masterKey.getSignaturesForID(userId))) { + WrappedSignature cert = new WrappedSignature(zert); + long certId = cert.getKeyId(); + + // If this is a foreign signature, never mind + if (certId != masterKeyId) { + continue; + } + + // Otherwise, first make sure it checks out + try { + cert.init(masterKey); + if (!cert.verifySignature(masterKey, userId)) { + log.add(LogLevel.WARN, LogType.MSG_KC_UID_BAD, + new String[] { userId }, indent); + modified = PGPPublicKey.removeCertification(modified, userId, zert); + removedCerts += 1; + continue; + } + } catch (PgpGeneralException e) { + log.add(LogLevel.WARN, LogType.MSG_KC_UID_BAD_ERR, + new String[] { userId }, indent); + modified = PGPPublicKey.removeCertification(modified, userId, zert); + removedCerts += 1; + continue; + } + + switch (zert.getSignatureType()) { + case PGPSignature.DEFAULT_CERTIFICATION: + case PGPSignature.NO_CERTIFICATION: + case PGPSignature.CASUAL_CERTIFICATION: + case PGPSignature.POSITIVE_CERTIFICATION: + if (selfCert == null) { + selfCert = zert; + } else if (selfCert.getCreationTime().before(cert.getCreationTime())) { + modified = PGPPublicKey.removeCertification(modified, userId, selfCert); + removedCerts += 1; + log.add(LogLevel.INFO, LogType.MSG_KC_UID_DUP, + new String[] { userId }, indent); + selfCert = zert; + } else { + modified = PGPPublicKey.removeCertification(modified, userId, zert); + removedCerts += 1; + log.add(LogLevel.INFO, LogType.MSG_KC_UID_DUP, + new String[] { userId }, indent); + } + // If there is a revocation certificate, and it's older than this, drop it + if (revocation != null + && revocation.getCreationTime().before(selfCert.getCreationTime())) { + modified = PGPPublicKey.removeCertification(modified, userId, revocation); + revocation = null; + removedCerts += 1; + log.add(LogLevel.INFO, LogType.MSG_KC_UID_REVOKE_OLD, + new String[] { userId }, indent); + } + break; + + case PGPSignature.CERTIFICATION_REVOCATION: + // If this is older than the (latest) self cert, drop it + if (selfCert != null && selfCert.getCreationTime().after(zert.getCreationTime())) { + modified = PGPPublicKey.removeCertification(modified, userId, zert); + removedCerts += 1; + log.add(LogLevel.INFO, LogType.MSG_KC_UID_REVOKE_OLD, + new String[] { userId }, indent); + continue; + } + // first revocation? remember it. + if (revocation == null) { + revocation = zert; + // more revocations? at least one is superfluous, then. + } else if (revocation.getCreationTime().before(cert.getCreationTime())) { + modified = PGPPublicKey.removeCertification(modified, userId, revocation); + removedCerts += 1; + log.add(LogLevel.INFO, LogType.MSG_KC_UID_REVOKE_DUP, + new String[] { userId }, indent); + revocation = zert; + } else { + modified = PGPPublicKey.removeCertification(modified, userId, zert); + removedCerts += 1; + log.add(LogLevel.INFO, LogType.MSG_KC_UID_REVOKE_DUP, + new String[] { userId }, indent); + } + break; + + default: + log.add(LogLevel.WARN, LogType.MSG_KC_UID_UNKNOWN_CERT, + new String[] { + "0x" + Integer.toString(zert.getSignatureType(), 16), + userId + }, indent); + modified = PGPPublicKey.removeCertification(modified, userId, zert); + removedCerts += 1; + } + + } + } + + // Replace modified key in the keyring + ring = PGPPublicKeyRing.insertPublicKey(ring, modified); - /* - // Remove all non-verifying self certificates - for (PGPPublicKey key : new IterableIterator(mRing.getPublicKeys())) { + log.add(LogLevel.DEBUG, LogType.MSG_KC_MASTER_SUCCESS, null, indent); + indent -= 1; + + } - for (PGPSignature sig : new IterableIterator( - key.getSignaturesOfType(isMasterKey() ? PGPSignature.KEY_REVOCATION - : PGPSignature.SUBKEY_REVOCATION))) { - return true; + // Process all keys + // NOTE we rely here on the special property that this iterator is independent from the keyring! + for (PGPPublicKey key : new IterableIterator(ring.getPublicKeys())) { + // Only care about the master key here! + if (key.isMasterKey()) { + continue; + } + log.add(LogLevel.DEBUG, LogType.MSG_KC_SUBKEY, + new String[]{PgpKeyHelper.convertKeyIdToHex(key.getKeyID())}, indent); + indent += 1; + // A subkey needs exactly one subkey binding certificate, and optionally one revocation + // certificate. + PGPPublicKey modified = key; + PGPSignature cert = null, revocation = null; + for (PGPSignature zig : new IterableIterator(key.getSignatures())) { + // remove from keyring (for now) + modified = PGPPublicKey.removeCertification(modified, zig); + WrappedSignature sig = new WrappedSignature(zig); + int type = sig.getSignatureType(); + + // filter out bad key types... + if (sig.getKeyId() != masterKey.getKeyID()) { + log.add(LogLevel.WARN, LogType.MSG_KC_CERT_BAD_KEYID, null, indent); + continue; + } + if (type != PGPSignature.SUBKEY_BINDING && type != PGPSignature.SUBKEY_REVOCATION) { + log.add(LogLevel.WARN, LogType.MSG_KC_CERT_BAD_TYPE, new String[]{ + "0x" + Integer.toString(type, 16) + }, indent); + continue; + } + + // make sure the certificate checks out + try { + sig.init(masterKey); + if (!sig.verifySignature(masterKey, key)) { + log.add(LogLevel.WARN, LogType.MSG_KC_CERT_BAD, null, indent); + continue; + } + } catch (PgpGeneralException e) { + log.add(LogLevel.WARN, LogType.MSG_KC_CERT_BAD_ERR, null, indent); + continue; + } + + if (type == PGPSignature.SUBKEY_BINDING) { + // TODO verify primary key binding signature for signing keys! + // if we already have a cert, and this one is not newer: skip it + if (cert != null && cert.getCreationTime().before(sig.getCreationTime())) { + continue; + } + cert = zig; + // if this is newer than a possibly existing revocation, drop that one + if (revocation != null && cert.getCreationTime().after(revocation.getCreationTime())) { + revocation = null; + } + // it must be a revocation, then (we made sure above) + } else { + // if there is no binding (yet), or the revocation is newer than the binding: keep it + if (cert == null || cert.getCreationTime().before(sig.getCreationTime())) { + revocation = zig; + } + } } - }*/ + // it is not properly bound? error! + if (cert == null) { + ring = PGPPublicKeyRing.removePublicKey(ring, modified); - log.add(LogLevel.OK, LogType.MSG_KC_SUCCESS, null, 0); + log.add(LogLevel.ERROR, LogType.MSG_KC_SUBKEY_NO_CERT, + new String[]{PgpKeyHelper.convertKeyIdToHex(key.getKeyID())}, indent); + indent -= 1; + continue; + } - return this; + // re-add certification + modified = PGPPublicKey.addCertification(modified, cert); + // add revocation, if any + if (revocation != null) { + modified = PGPPublicKey.addCertification(modified, revocation); + } + // replace pubkey in keyring + ring = PGPPublicKeyRing.insertPublicKey(ring, modified); + log.add(LogLevel.DEBUG, LogType.MSG_KC_SUBKEY_SUCCESS, null, indent); + indent -= 1; + } + + if (removedCerts > 0) { + log.add(LogLevel.OK, LogType.MSG_KC_SUCCESS_REMOVED, + new String[] { Integer.toString(removedCerts) }, indent); + } else { + log.add(LogLevel.OK, LogType.MSG_KC_SUCCESS, null, indent); + } + return new UncachedKeyRing(ring); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java index 1b7a5e8ba..093b6c96f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java @@ -35,7 +35,7 @@ public class WrappedSignature { final PGPSignature mSig; - protected WrappedSignature(PGPSignature sig) { + WrappedSignature(PGPSignature sig) { mSig = sig; } @@ -88,7 +88,7 @@ public class WrappedSignature { init(key.getPublicKey()); } - protected void init(PGPPublicKey key) throws PgpGeneralException { + void init(PGPPublicKey key) throws PgpGeneralException { try { JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = new JcaPGPContentVerifierBuilderProvider() @@ -125,7 +125,27 @@ public class WrappedSignature { } } - protected boolean verifySignature(PGPPublicKey key, String uid) throws PgpGeneralException { + boolean verifySignature(PGPPublicKey key) throws PgpGeneralException { + try { + return mSig.verifyCertification(key); + } catch (SignatureException e) { + throw new PgpGeneralException("Sign!", e); + } catch (PGPException e) { + throw new PgpGeneralException("Error!", e); + } + } + + boolean verifySignature(PGPPublicKey masterKey, PGPPublicKey subKey) throws PgpGeneralException { + try { + return mSig.verifyCertification(masterKey, subKey); + } catch (SignatureException e) { + throw new PgpGeneralException("Sign!", e); + } catch (PGPException e) { + throw new PgpGeneralException("Error!", e); + } + } + + boolean verifySignature(PGPPublicKey key, String uid) throws PgpGeneralException { try { return mSig.verifyCertification(uid, key); } catch (SignatureException e) { 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 40af285e1..6c004f19a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -283,7 +283,7 @@ public class ProviderHelper { mIndent += 1; // Canonicalize this key, to assert a number of assumptions made about it. - keyRing = keyRing.canonicalize(mLog); + keyRing = keyRing.canonicalize(mLog, mIndent); UncachedPublicKey masterKey = keyRing.getPublicKey(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index 67ff8318b..48f40026e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -157,8 +157,27 @@ public class OperationResultParcel implements Parcelable { MSG_IS_SUCCESS (R.string.msg_is_success), // keyring canonicalization - MSG_KC(R.string.msg_kc), - MSG_KC_SUCCESS(R.string.msg_kc_success), + MSG_KC (R.string.msg_kc), + MSG_KC_CERT_BAD_ERR (R.string.msg_kc_cert_bad_err), + MSG_KC_CERT_BAD_KEYID (R.string.msg_kc_cert_bad_keyid), + MSG_KC_CERT_BAD (R.string.msg_kc_cert_bad), + MSG_KC_CERT_BAD_TYPE (R.string.msg_kc_cert_bad_type), + MSG_KC_MASTER (R.string.msg_kc_master), + MSG_KC_MASTER_SUCCESS (R.string.msg_kc_master_success), + MSG_KC_REVOKE_BAD_ERR (R.string.msg_kc_revoke_bad_err), + MSG_KC_REVOKE_BAD (R.string.msg_kc_revoke_bad), + MSG_KC_REVOKE_DUP (R.string.msg_kc_revoke_dup), + MSG_KC_SUBKEY_NO_CERT (R.string.msg_kc_subkey_no_cert), + MSG_KC_SUBKEY (R.string.msg_kc_subkey), + MSG_KC_SUBKEY_SUCCESS (R.string.msg_kc_subkey_success), + MSG_KC_SUCCESS_REMOVED (R.string.msg_kc_success_removed), + MSG_KC_SUCCESS (R.string.msg_kc_success), + MSG_KC_UID_BAD_ERR (R.string.msg_kc_uid_bad_err), + MSG_KC_UID_BAD (R.string.msg_kc_uid_bad), + MSG_KC_UID_DUP (R.string.msg_kc_uid_dup), + MSG_KC_UID_REVOKE_DUP (R.string.msg_kc_uid_revoke_dup), + MSG_KC_UID_REVOKE_OLD (R.string.msg_kc_uid_revoke_old), + MSG_KC_UID_UNKNOWN_CERT (R.string.msg_kc_uid_unknown_cert), ; private final int mMsgId; -- cgit v1.2.3 From dae503284f47eb7e5eed71140f9fceaa2ff420c2 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 12 Jun 2014 17:38:48 +0200 Subject: canonicalize: more stuff --- .../keychain/keyimport/ParcelableKeyRing.java | 2 +- .../keychain/pgp/UncachedKeyRing.java | 79 +++++++++++++--------- .../keychain/service/OperationResultParcel.java | 17 +++-- .../keychain/ui/ImportKeysActivity.java | 2 + 4 files changed, 60 insertions(+), 40 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableKeyRing.java index 5da6c4cd3..fdf561aaf 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableKeyRing.java @@ -3,7 +3,7 @@ package org.sufficientlysecure.keychain.keyimport; import android.os.Parcel; import android.os.Parcelable; -/** This is a trivial wrapper around UncachedKeyRing which implements Parcelable. It exists +/** This is a trivial wrapper around keyring bytes which implements Parcelable. It exists * for the sole purpose of keeping spongycastle and android imports in separate packages. */ public class ParcelableKeyRing implements Parcelable { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java index 215c17590..a8e4820cf 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java @@ -15,7 +15,6 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog; import org.sufficientlysecure.keychain.service.OperationResultParcel.LogLevel; import org.sufficientlysecure.keychain.service.OperationResultParcel.LogType; -import org.sufficientlysecure.keychain.service.OperationResults.SaveKeyringResult; import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.Log; @@ -24,7 +23,6 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -229,7 +227,7 @@ public class UncachedKeyRing { if (type != PGPSignature.KEY_REVOCATION) { // Unknown type, just remove - log.add(LogLevel.WARN, LogType.MSG_KC_CERT_BAD_TYPE, new String[]{ + log.add(LogLevel.WARN, LogType.MSG_KC_SUB_BAD_TYPE, new String[]{ "0x" + Integer.toString(type, 16) }, indent); modified = PGPPublicKey.removeCertification(modified, zert); @@ -380,81 +378,98 @@ public class UncachedKeyRing { } // Process all keys - // NOTE we rely here on the special property that this iterator is independent from the keyring! for (PGPPublicKey key : new IterableIterator(ring.getPublicKeys())) { - // Only care about the master key here! + // Don't care about the master key here, that one gets special treatment above if (key.isMasterKey()) { continue; } - log.add(LogLevel.DEBUG, LogType.MSG_KC_SUBKEY, + log.add(LogLevel.DEBUG, LogType.MSG_KC_SUB, new String[]{PgpKeyHelper.convertKeyIdToHex(key.getKeyID())}, indent); indent += 1; // A subkey needs exactly one subkey binding certificate, and optionally one revocation // certificate. PGPPublicKey modified = key; - PGPSignature cert = null, revocation = null; + PGPSignature selfCert = null, revocation = null; for (PGPSignature zig : new IterableIterator(key.getSignatures())) { // remove from keyring (for now) modified = PGPPublicKey.removeCertification(modified, zig); - WrappedSignature sig = new WrappedSignature(zig); - int type = sig.getSignatureType(); + WrappedSignature cert = new WrappedSignature(zig); + int type = cert.getSignatureType(); // filter out bad key types... - if (sig.getKeyId() != masterKey.getKeyID()) { - log.add(LogLevel.WARN, LogType.MSG_KC_CERT_BAD_KEYID, null, indent); + if (cert.getKeyId() != masterKey.getKeyID()) { + log.add(LogLevel.WARN, LogType.MSG_KC_SUB_BAD_KEYID, null, indent); continue; } if (type != PGPSignature.SUBKEY_BINDING && type != PGPSignature.SUBKEY_REVOCATION) { - log.add(LogLevel.WARN, LogType.MSG_KC_CERT_BAD_TYPE, new String[]{ + log.add(LogLevel.WARN, LogType.MSG_KC_SUB_BAD_TYPE, new String[]{ "0x" + Integer.toString(type, 16) }, indent); continue; } - // make sure the certificate checks out - try { - sig.init(masterKey); - if (!sig.verifySignature(masterKey, key)) { - log.add(LogLevel.WARN, LogType.MSG_KC_CERT_BAD, null, indent); + if (type == PGPSignature.SUBKEY_BINDING) { + // TODO verify primary key binding signature for signing keys! + + // make sure the certificate checks out + try { + cert.init(masterKey); + if (!cert.verifySignature(masterKey, key)) { + log.add(LogLevel.WARN, LogType.MSG_KC_SUB_BAD, null, indent); + log.add(LogLevel.WARN, LogType.MSG_KC_SUB, new String[] { + cert.getCreationTime().toString() + }, indent); + continue; + } + } catch (PgpGeneralException e) { + log.add(LogLevel.WARN, LogType.MSG_KC_SUB_BAD_ERR, null, indent); continue; } - } catch (PgpGeneralException e) { - log.add(LogLevel.WARN, LogType.MSG_KC_CERT_BAD_ERR, null, indent); - continue; - } - if (type == PGPSignature.SUBKEY_BINDING) { - // TODO verify primary key binding signature for signing keys! // if we already have a cert, and this one is not newer: skip it - if (cert != null && cert.getCreationTime().before(sig.getCreationTime())) { + if (selfCert != null && selfCert.getCreationTime().before(cert.getCreationTime())) { continue; } - cert = zig; + selfCert = zig; // if this is newer than a possibly existing revocation, drop that one - if (revocation != null && cert.getCreationTime().after(revocation.getCreationTime())) { + if (revocation != null && selfCert.getCreationTime().after(revocation.getCreationTime())) { revocation = null; } - // it must be a revocation, then (we made sure above) + + // it must be a revocation, then (we made sure above) } else { + + // make sure the certificate checks out + try { + cert.init(masterKey); + if (!cert.verifySignature(key)) { + log.add(LogLevel.WARN, LogType.MSG_KC_SUB_REVOKE_BAD, null, indent); + continue; + } + } catch (PgpGeneralException e) { + log.add(LogLevel.WARN, LogType.MSG_KC_SUB_REVOKE_BAD_ERR, null, indent); + continue; + } + // if there is no binding (yet), or the revocation is newer than the binding: keep it - if (cert == null || cert.getCreationTime().before(sig.getCreationTime())) { + if (selfCert == null || selfCert.getCreationTime().before(cert.getCreationTime())) { revocation = zig; } } } // it is not properly bound? error! - if (cert == null) { + if (selfCert == null) { ring = PGPPublicKeyRing.removePublicKey(ring, modified); - log.add(LogLevel.ERROR, LogType.MSG_KC_SUBKEY_NO_CERT, + log.add(LogLevel.ERROR, LogType.MSG_KC_SUB_NO_CERT, new String[]{PgpKeyHelper.convertKeyIdToHex(key.getKeyID())}, indent); indent -= 1; continue; } // re-add certification - modified = PGPPublicKey.addCertification(modified, cert); + modified = PGPPublicKey.addCertification(modified, selfCert); // add revocation, if any if (revocation != null) { modified = PGPPublicKey.addCertification(modified, revocation); @@ -462,7 +477,7 @@ public class UncachedKeyRing { // replace pubkey in keyring ring = PGPPublicKeyRing.insertPublicKey(ring, modified); - log.add(LogLevel.DEBUG, LogType.MSG_KC_SUBKEY_SUCCESS, null, indent); + log.add(LogLevel.DEBUG, LogType.MSG_KC_SUB_SUCCESS, null, indent); indent -= 1; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index 48f40026e..5c223e870 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -158,18 +158,21 @@ public class OperationResultParcel implements Parcelable { // keyring canonicalization MSG_KC (R.string.msg_kc), - MSG_KC_CERT_BAD_ERR (R.string.msg_kc_cert_bad_err), - MSG_KC_CERT_BAD_KEYID (R.string.msg_kc_cert_bad_keyid), - MSG_KC_CERT_BAD (R.string.msg_kc_cert_bad), - MSG_KC_CERT_BAD_TYPE (R.string.msg_kc_cert_bad_type), MSG_KC_MASTER (R.string.msg_kc_master), MSG_KC_MASTER_SUCCESS (R.string.msg_kc_master_success), MSG_KC_REVOKE_BAD_ERR (R.string.msg_kc_revoke_bad_err), MSG_KC_REVOKE_BAD (R.string.msg_kc_revoke_bad), MSG_KC_REVOKE_DUP (R.string.msg_kc_revoke_dup), - MSG_KC_SUBKEY_NO_CERT (R.string.msg_kc_subkey_no_cert), - MSG_KC_SUBKEY (R.string.msg_kc_subkey), - MSG_KC_SUBKEY_SUCCESS (R.string.msg_kc_subkey_success), + MSG_KC_SUB (R.string.msg_kc_sub), + MSG_KC_SUB_BAD_ERR(R.string.msg_kc_sub_bad_err), + MSG_KC_SUB_BAD_KEYID(R.string.msg_kc_sub_bad_keyid), + MSG_KC_SUB_BAD(R.string.msg_kc_sub_bad), + MSG_KC_SUB_BAD_TYPE(R.string.msg_kc_sub_bad_type), + MSG_KC_SUB_NO_CERT(R.string.msg_kc_sub_no_cert), + MSG_KC_SUB_REVOKE_BAD_ERR (R.string.msg_kc_sub_revoke_bad_err), + MSG_KC_SUB_REVOKE_BAD (R.string.msg_kc_sub_revoke_bad), + MSG_KC_SUB_REVOKE_DUP (R.string.msg_kc_sub_revoke_dup), + MSG_KC_SUB_SUCCESS (R.string.msg_kc_sub_success), MSG_KC_SUCCESS_REMOVED (R.string.msg_kc_success_removed), MSG_KC_SUCCESS (R.string.msg_kc_success), MSG_KC_UID_BAD_ERR (R.string.msg_kc_uid_bad_err), diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java index 7fa18406c..f389726ff 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java @@ -456,10 +456,12 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O } */ + /* if (ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN.equals(getIntent().getAction())) { ImportKeysActivity.this.setResult(Activity.RESULT_OK, mPendingIntentData); finish(); } + */ } } }; -- cgit v1.2.3 From e4a7d4f6e5dc6eb0acac2aa4945852ae2f1d8bb8 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 12 Jun 2014 18:10:48 +0200 Subject: import-log: minor improvements --- .../keychain/pgp/PgpImportExport.java | 11 +- .../keychain/pgp/UncachedKeyRing.java | 3 - .../keychain/provider/ProviderHelper.java | 113 ++++++++++++--------- .../keychain/service/OperationResultParcel.java | 2 + 4 files changed, 74 insertions(+), 55 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java index bb45cc7db..e1967429a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java @@ -127,9 +127,7 @@ public class PgpImportExport { updateProgress(R.string.progress_importing, 0, 100); - int newKeys = 0; - int oldKeys = 0; - int badKeys = 0; + int newKeys = 0, oldKeys = 0, badKeys = 0; int position = 0; for (ParcelableKeyRing entry : entries) { @@ -147,7 +145,12 @@ public class PgpImportExport { } } - SaveKeyringResult result = mProviderHelper.savePublicKeyRing(key); + SaveKeyringResult result; + if (key.isSecret()) { + result = mProviderHelper.saveSecretKeyRing(key); + } else { + result = mProviderHelper.savePublicKeyRing(key); + } if (!result.success()) { badKeys += 1; } else if (result.updated()) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java index a8e4820cf..1edc529c6 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java @@ -416,9 +416,6 @@ public class UncachedKeyRing { cert.init(masterKey); if (!cert.verifySignature(masterKey, key)) { log.add(LogLevel.WARN, LogType.MSG_KC_SUB_BAD, null, indent); - log.add(LogLevel.WARN, LogType.MSG_KC_SUB, new String[] { - cert.getCreationTime().toString() - }, indent); continue; } } catch (PgpGeneralException e) { 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 6c004f19a..519d5ee0f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -296,20 +296,14 @@ public class ProviderHelper { secretRing = null; } - // delete old version of this keyRing, which also deletes all keys and userIds on cascade - int deleted = mContentResolver.delete( - KeyRingData.buildPublicKeyRingUri(Long.toString(masterKeyId)), null, null); - if (deleted > 0) { - log(LogLevel.DEBUG, LogType.MSG_IP_DELETE_OLD_OK); - result |= SaveKeyringResult.UPDATED; - } else { - log(LogLevel.DEBUG, LogType.MSG_IP_DELETE_OLD_FAIL); - } - + ArrayList operations; try { + log(LogLevel.DEBUG, LogType.MSG_IP_PREPARE); + mIndent += 1; + // save all keys and userIds included in keyRing object in database - ArrayList operations = new ArrayList(); + operations = new ArrayList(); log(LogLevel.INFO, LogType.MSG_IP_INSERT_KEYRING); { // insert keyring @@ -354,26 +348,26 @@ public class ProviderHelper { values.put(Keys.IS_REVOKED, key.isRevoked()); if (c) { if (e) { - log(LogLevel.DEBUG,s ? LogType.MSG_IP_SUBKEY_FLAGS_CES - : LogType.MSG_IP_SUBKEY_FLAGS_CEX, null); + log(LogLevel.DEBUG, s ? LogType.MSG_IP_SUBKEY_FLAGS_CES + : LogType.MSG_IP_SUBKEY_FLAGS_CEX, null); } else { log(LogLevel.DEBUG, s ? LogType.MSG_IP_SUBKEY_FLAGS_CXS - : LogType.MSG_IP_SUBKEY_FLAGS_CXX, null); + : LogType.MSG_IP_SUBKEY_FLAGS_CXX, null); } } else { if (e) { log(LogLevel.DEBUG, s ? LogType.MSG_IP_SUBKEY_FLAGS_XES - : LogType.MSG_IP_SUBKEY_FLAGS_XEX, null); + : LogType.MSG_IP_SUBKEY_FLAGS_XEX, null); } else { log(LogLevel.DEBUG, s ? LogType.MSG_IP_SUBKEY_FLAGS_XXS - : LogType.MSG_IP_SUBKEY_FLAGS_XXX, null); + : LogType.MSG_IP_SUBKEY_FLAGS_XXX, null); } } Date creation = key.getCreationTime(); values.put(Keys.CREATION, creation.getTime() / 1000); if (creation.after(new Date())) { - log(LogLevel.ERROR, LogType.MSG_IP_SUBKEY_FUTURE, new String[] { + log(LogLevel.ERROR, LogType.MSG_IP_SUBKEY_FUTURE, new String[]{ creation.toString() }); return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); @@ -382,11 +376,11 @@ public class ProviderHelper { if (expiryDate != null) { values.put(Keys.EXPIRY, expiryDate.getTime() / 1000); if (key.isExpired()) { - log(LogLevel.INFO, LogType.MSG_IP_SUBKEY_EXPIRED, new String[] { + log(LogLevel.DEBUG, LogType.MSG_IP_SUBKEY_EXPIRED, new String[]{ expiryDate.toString() }); } else { - log(LogLevel.DEBUG, LogType.MSG_IP_SUBKEY_EXPIRES, new String[] { + log(LogLevel.DEBUG, LogType.MSG_IP_SUBKEY_EXPIRES, new String[]{ expiryDate.toString() }); } @@ -402,8 +396,8 @@ public class ProviderHelper { // get a list of owned secret keys, for verification filtering LongSparseArray trustedKeys = getUncachedMasterKeys(KeyRingData.buildSecretKeyRingUri()); - log(LogLevel.INFO, LogType.MSG_IP_TRUST_USING, new String[] { - Integer.toString(trustedKeys.size()) + log(LogLevel.INFO, LogType.MSG_IP_TRUST_USING, new String[]{ + Integer.toString(trustedKeys.size()) }); // classify and order user ids. primary are moved to the front, revoked to the back, @@ -419,7 +413,7 @@ public class ProviderHelper { int unknownCerts = 0; - log(LogLevel.INFO, LogType.MSG_IP_UID_PROCESSING, new String[] { userId }); + log(LogLevel.INFO, LogType.MSG_IP_UID_PROCESSING, new String[]{ userId }); mIndent += 1; // look through signatures for this specific key for (WrappedSignature cert : new IterableIterator( @@ -453,7 +447,7 @@ public class ProviderHelper { item.isPrimary = cert.isPrimaryUserId(); if (cert.isRevocation()) { item.isRevoked = true; - log(LogLevel.INFO, LogType.MSG_IP_UID_REVOKED); + log(LogLevel.DEBUG, LogType.MSG_IP_UID_REVOKED); } else { item.isRevoked = false; } @@ -467,7 +461,8 @@ public class ProviderHelper { if (cert.verifySignature(masterKey, userId)) { item.trustedCerts.add(cert); log(LogLevel.INFO, LogType.MSG_IP_UID_CERT_GOOD, new String[] { - PgpKeyHelper.convertKeyIdToHex(trustedKey.getKeyId()) + PgpKeyHelper.convertKeyIdToHexShort(trustedKey.getKeyId()), + trustedKey.getPrimaryUserId() }); } else { log(LogLevel.WARN, LogType.MSG_IP_UID_CERT_BAD); @@ -485,7 +480,7 @@ public class ProviderHelper { mIndent -= 1; if (unknownCerts > 0) { - log(LogLevel.DEBUG, LogType.MSG_IP_UID_CERTS_UNKNOWN, new String[] { + log(LogLevel.DEBUG, LogType.MSG_IP_UID_CERTS_UNKNOWN, new String[]{ Integer.toString(unknownCerts) }); } @@ -517,13 +512,43 @@ public class ProviderHelper { } } - log(LogLevel.DEBUG, LogType.MSG_IP_APPLY_BATCH); - mContentResolver.applyBatch(KeychainContract.CONTENT_AUTHORITY, operations); + log(LogLevel.DEBUG, LogType.MSG_IP_PREPARE_SUCCESS); + mIndent -= 1; + } catch (IOException e) { log(LogLevel.ERROR, LogType.MSG_IP_FAIL_IO_EXC); Log.e(Constants.TAG, "IOException during import", e); mIndent -= 1; return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + } + + try { + // delete old version of this keyRing, which also deletes all keys and userIds on cascade + int deleted = mContentResolver.delete( + KeyRingData.buildPublicKeyRingUri(Long.toString(masterKeyId)), null, null); + if (deleted > 0) { + log(LogLevel.DEBUG, LogType.MSG_IP_DELETE_OLD_OK); + result |= SaveKeyringResult.UPDATED; + } else { + log(LogLevel.DEBUG, LogType.MSG_IP_DELETE_OLD_FAIL); + } + + log(LogLevel.DEBUG, LogType.MSG_IP_APPLY_BATCH); + mContentResolver.applyBatch(KeychainContract.CONTENT_AUTHORITY, operations); + + // Save the saved keyring (if any) + if (secretRing != null) { + log(LogLevel.DEBUG, LogType.MSG_IP_REINSERT_SECRET); + mIndent += 1; + saveSecretKeyRing(secretRing); + result |= SaveKeyringResult.SAVED_SECRET; + mIndent -= 1; + } + + mIndent -= 1; + log(LogLevel.OK, LogType.MSG_IP_SUCCESS); + return new SaveKeyringResult(result, mLog); + } catch (RemoteException e) { log(LogLevel.ERROR, LogType.MSG_IP_FAIL_REMOTE_EX); Log.e(Constants.TAG, "RemoteException during import", e); @@ -536,19 +561,6 @@ public class ProviderHelper { return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); } - // Save the saved keyring (if any) - if (secretRing != null) { - log(LogLevel.DEBUG, LogType.MSG_IP_REINSERT_SECRET); - mIndent += 1; - saveSecretKeyRing(secretRing); - result |= SaveKeyringResult.SAVED_SECRET; - mIndent -= 1; - } - - mIndent -= 1; - log(LogLevel.OK, LogType.MSG_IP_SUCCESS); - return new SaveKeyringResult(result, mLog); - } private static class UserIdItem implements Comparable { @@ -575,18 +587,23 @@ public class ProviderHelper { /** * Saves a PGPSecretKeyRing in the DB. This will only work if a corresponding public keyring * is already in the database! + * + * TODO allow adding secret keys where no public key exists (ie, consolidate keys) */ - public OperationResultParcel saveSecretKeyRing(UncachedKeyRing keyRing) { + public SaveKeyringResult saveSecretKeyRing(UncachedKeyRing keyRing) { + + if (!keyRing.isSecret()) { + log(LogLevel.ERROR, LogType.MSG_IS_BAD_TYPE_PUBLIC); + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + } long masterKeyId = keyRing.getMasterKeyId(); log(LogLevel.START, LogType.MSG_IS, - new String[]{PgpKeyHelper.convertKeyIdToHex(masterKeyId)}); + new String[]{ PgpKeyHelper.convertKeyIdToHex(masterKeyId) }); mIndent += 1; - if (!keyRing.isSecret()) { - log(LogLevel.ERROR, LogType.MSG_IS_BAD_TYPE_PUBLIC); - return new OperationResultParcel(1, mLog); - } + // IF this is successful, it's a secret key + int result = SaveKeyringResult.SAVED_SECRET; // save secret keyring try { @@ -599,7 +616,7 @@ public class ProviderHelper { } catch (IOException e) { Log.e(Constants.TAG, "Failed to encode key!", e); log(LogLevel.ERROR, LogType.MSG_IS_IO_EXCPTION); - return new OperationResultParcel(1, mLog); + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); } { @@ -643,7 +660,7 @@ public class ProviderHelper { } log(LogLevel.OK, LogType.MSG_IS_SUCCESS); - return new OperationResultParcel(0, mLog); + return new SaveKeyringResult(result, mLog); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index 5c223e870..9790d216d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -115,6 +115,8 @@ public class OperationResultParcel implements Parcelable { MSG_IP_FAIL_REMOTE_EX (R.string.msg_ip_fail_remote_ex), MSG_IP_INSERT_KEYRING (R.string.msg_ip_insert_keyring), MSG_IP_INSERT_SUBKEYS (R.string.msg_ip_insert_subkeys), + MSG_IP_PREPARE (R.string.msg_ip_prepare), + MSG_IP_PREPARE_SUCCESS(R.string.msg_ip_prepare_success), MSG_IP_PRESERVING_SECRET (R.string.msg_ip_preserving_secret), MSG_IP_REINSERT_SECRET (R.string.msg_ip_reinsert_secret), MSG_IP_SUBKEY (R.string.msg_ip_subkey), -- cgit v1.2.3 From 59701250ba442b12d98f7b328a4cc61db1fe9158 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 12 Jun 2014 18:11:31 +0200 Subject: import: use wrapped keyring instead of uncached keyring for trust --- .../keychain/provider/ProviderHelper.java | 44 ++++++++++------------ 1 file changed, 19 insertions(+), 25 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') 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 519d5ee0f..1c68a7b0b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -29,7 +29,7 @@ import android.support.v4.util.LongSparseArray; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.pgp.KeyRing; -import org.sufficientlysecure.keychain.service.OperationResultParcel; +import org.sufficientlysecure.keychain.pgp.WrappedPublicKey; import org.sufficientlysecure.keychain.service.OperationResultParcel.LogType; import org.sufficientlysecure.keychain.service.OperationResultParcel.LogLevel; import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog; @@ -172,36 +172,31 @@ public class ProviderHelper { } } - public Object getUnifiedData(long masterKeyId, String column, int type) - throws NotFoundException { - return getUnifiedData(masterKeyId, new String[]{column}, new int[]{type}).get(column); - } - public HashMap getUnifiedData(long masterKeyId, String[] proj, int[] types) throws NotFoundException { return getGenericData(KeyRings.buildUnifiedKeyRingUri(masterKeyId), proj, types); } - private LongSparseArray getUncachedMasterKeys(Uri queryUri) { - Cursor cursor = mContentResolver.query(queryUri, - new String[]{KeyRingData.MASTER_KEY_ID, KeyRingData.KEY_RING_DATA}, - null, null, null); + private LongSparseArray getAllWrappedMasterKeys() { + Cursor cursor = mContentResolver.query(KeyRings.buildUnifiedKeyRingsUri(), new String[] { + KeyRings.MASTER_KEY_ID, + // we pick from cache only information that is not easily available from keyrings + KeyRings.HAS_ANY_SECRET, KeyRings.VERIFIED, + // and of course, ring data + KeyRings.PUBKEY_DATA + }, KeyRings.HAS_ANY_SECRET + " = 1", null, null); - LongSparseArray result = - new LongSparseArray(cursor.getCount()); + LongSparseArray result = + new LongSparseArray(cursor.getCount()); try { if (cursor != null && cursor.moveToFirst()) do { long masterKeyId = cursor.getLong(0); - byte[] data = cursor.getBlob(1); - if (data != null) { - try { - result.put(masterKeyId, - UncachedKeyRing.decodeFromData(data).getPublicKey()); - } catch(PgpGeneralException e) { - Log.e(Constants.TAG, "Error parsing keyring, skipping " + masterKeyId, e); - } catch(IOException e) { - Log.e(Constants.TAG, "IO error, skipping keyring" + masterKeyId, e); - } + boolean hasAnySecret = cursor.getInt(1) > 0; + int verified = cursor.getInt(2); + byte[] blob = cursor.getBlob(3); + if (blob != null) { + result.put(masterKeyId, + new WrappedPublicKeyRing(blob, hasAnySecret, verified).getSubkey()); } } while (cursor.moveToNext()); } finally { @@ -394,8 +389,7 @@ public class ProviderHelper { mIndent -= 1; // get a list of owned secret keys, for verification filtering - LongSparseArray trustedKeys = - getUncachedMasterKeys(KeyRingData.buildSecretKeyRingUri()); + LongSparseArray trustedKeys = getAllWrappedMasterKeys(); log(LogLevel.INFO, LogType.MSG_IP_TRUST_USING, new String[]{ Integer.toString(trustedKeys.size()) }); @@ -456,7 +450,7 @@ public class ProviderHelper { // verify signatures from known private keys if (trustedKeys.indexOfKey(certId) >= 0) { - UncachedPublicKey trustedKey = trustedKeys.get(certId); + WrappedPublicKey trustedKey = trustedKeys.get(certId); cert.init(trustedKey); if (cert.verifySignature(masterKey, userId)) { item.trustedCerts.add(cert); -- cgit v1.2.3 From 9dd40b7238826ec05dc3162c58cfa90909b1c5ad Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 12 Jun 2014 18:17:35 +0200 Subject: import-log: more minor improvements --- .../sufficientlysecure/keychain/provider/ProviderHelper.java | 11 +++++------ .../keychain/service/OperationResultParcel.java | 4 +--- 2 files changed, 6 insertions(+), 9 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') 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 1c68a7b0b..96bf3f207 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -390,13 +390,12 @@ public class ProviderHelper { // get a list of owned secret keys, for verification filtering LongSparseArray trustedKeys = getAllWrappedMasterKeys(); - log(LogLevel.INFO, LogType.MSG_IP_TRUST_USING, new String[]{ - Integer.toString(trustedKeys.size()) - }); // classify and order user ids. primary are moved to the front, revoked to the back, // otherwise the order in the keyfile is preserved. - log(LogLevel.DEBUG, LogType.MSG_IP_UID_CLASSIFYING); + log(LogLevel.INFO, LogType.MSG_IP_UID_CLASSIFYING, new String[]{ + Integer.toString(trustedKeys.size()) + }); mIndent += 1; List uids = new ArrayList(); for (String userId : new IterableIterator( @@ -471,18 +470,18 @@ public class ProviderHelper { }); } } - mIndent -= 1; if (unknownCerts > 0) { log(LogLevel.DEBUG, LogType.MSG_IP_UID_CERTS_UNKNOWN, new String[]{ Integer.toString(unknownCerts) }); } + mIndent -= 1; } mIndent -= 1; - log(LogLevel.INFO, LogType.MSG_IP_UID_INSERT); + log(LogLevel.DEBUG, LogType.MSG_IP_UID_REORDER); // primary before regular before revoked (see UserIdItem.compareTo) // this is a stable sort, so the order of keys is otherwise preserved. Collections.sort(uids); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index 9790d216d..f83dc796f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -133,14 +133,12 @@ public class OperationResultParcel implements Parcelable { MSG_IP_SUBKEY_FLAGS_XXX (R.string.msg_ip_subkey_flags_xxx), MSG_IP_SUBKEY_FUTURE (R.string.msg_ip_subkey_future), MSG_IP_SUCCESS (R.string.msg_ip_success), - MSG_IP_TRUST_RETRIEVE (R.string.msg_ip_trust_retrieve), - MSG_IP_TRUST_USING (R.string.msg_ip_trust_using), MSG_IP_UID_CERT_BAD (R.string.msg_ip_uid_cert_bad), MSG_IP_UID_CERT_ERROR (R.string.msg_ip_uid_cert_error), MSG_IP_UID_CERT_GOOD (R.string.msg_ip_uid_cert_good), MSG_IP_UID_CERTS_UNKNOWN (R.string.msg_ip_uid_certs_unknown), MSG_IP_UID_CLASSIFYING (R.string.msg_ip_uid_classifying), - MSG_IP_UID_INSERT (R.string.msg_ip_uid_insert), + MSG_IP_UID_REORDER(R.string.msg_ip_uid_reorder), MSG_IP_UID_PROCESSING (R.string.msg_ip_uid_processing), MSG_IP_UID_REVOKED (R.string.msg_ip_uid_revoked), MSG_IP_UID_SELF_BAD (R.string.msg_ip_uid_self_bad), -- cgit v1.2.3 From 0594d9156ef148e3dd4ad7301cb4c036893dd383 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 12 Jun 2014 21:57:03 +0200 Subject: canonicalize: filter out future and local certificates --- .../keychain/pgp/UncachedKeyRing.java | 80 +++++++++++++++++++--- .../keychain/pgp/WrappedSignature.java | 7 ++ .../keychain/service/OperationResultParcel.java | 11 ++- 3 files changed, 85 insertions(+), 13 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java index 1edc529c6..8019b2b52 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java @@ -2,6 +2,7 @@ package org.sufficientlysecure.keychain.pgp; import org.spongycastle.bcpg.ArmoredOutputStream; import org.spongycastle.bcpg.S2K; +import org.spongycastle.bcpg.SignatureSubpacketTags; import org.spongycastle.openpgp.PGPKeyRing; import org.spongycastle.openpgp.PGPObjectFactory; import org.spongycastle.openpgp.PGPPublicKey; @@ -23,6 +24,7 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.Date; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -178,6 +180,7 @@ public class UncachedKeyRing { * * More specifically: * - Remove all non-verifying self-certificates + * - Remove all "future" self-certificates * - Remove all certificates flagged as "local" * - Remove all certificates which are superseded by a newer one on the same target * @@ -200,6 +203,8 @@ public class UncachedKeyRing { new String[]{PgpKeyHelper.convertKeyIdToHex(getMasterKeyId())}, indent); indent += 1; + final Date now = new Date(); + int removedCerts = 0; PGPPublicKeyRing ring = (PGPPublicKeyRing) mRing; @@ -215,6 +220,7 @@ public class UncachedKeyRing { PGPSignature revocation = null; for (PGPSignature zert : new IterableIterator(masterKey.getSignatures())) { int type = zert.getSignatureType(); + // Disregard certifications on user ids, we will deal with those later if (type == PGPSignature.NO_CERTIFICATION || type == PGPSignature.DEFAULT_CERTIFICATION @@ -227,7 +233,7 @@ public class UncachedKeyRing { if (type != PGPSignature.KEY_REVOCATION) { // Unknown type, just remove - log.add(LogLevel.WARN, LogType.MSG_KC_SUB_BAD_TYPE, new String[]{ + log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD_TYPE, new String[]{ "0x" + Integer.toString(type, 16) }, indent); modified = PGPPublicKey.removeCertification(modified, zert); @@ -235,6 +241,22 @@ public class UncachedKeyRing { continue; } + if (cert.getCreationTime().after(now)) { + // Creation date in the future? No way! + log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD_TIME, null, indent); + modified = PGPPublicKey.removeCertification(modified, zert); + removedCerts += 1; + continue; + } + + if (cert.isLocal()) { + // Creation date in the future? No way! + log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD_LOCAL, null, indent); + modified = PGPPublicKey.removeCertification(modified, zert); + removedCerts += 1; + continue; + } + try { cert.init(masterKey); if (!cert.verifySignature(masterKey)) { @@ -276,7 +298,38 @@ public class UncachedKeyRing { WrappedSignature cert = new WrappedSignature(zert); long certId = cert.getKeyId(); - // If this is a foreign signature, never mind + + int type = zert.getSignatureType(); + if (type != PGPSignature.DEFAULT_CERTIFICATION + && type != PGPSignature.NO_CERTIFICATION + && type != PGPSignature.CASUAL_CERTIFICATION + && type != PGPSignature.POSITIVE_CERTIFICATION + && type != PGPSignature.CERTIFICATION_REVOCATION) { + log.add(LogLevel.WARN, LogType.MSG_KC_UID_BAD_TYPE, + new String[] { + "0x" + Integer.toString(zert.getSignatureType(), 16) + }, indent); + modified = PGPPublicKey.removeCertification(modified, userId, zert); + removedCerts += 1; + } + + if (cert.getCreationTime().after(now)) { + // Creation date in the future? No way! + log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD_TIME, null, indent); + modified = PGPPublicKey.removeCertification(modified, zert); + removedCerts += 1; + continue; + } + + if (cert.isLocal()) { + // Creation date in the future? No way! + log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD_LOCAL, null, indent); + modified = PGPPublicKey.removeCertification(modified, zert); + removedCerts += 1; + continue; + } + + // If this is a foreign signature, never mind any further if (certId != masterKeyId) { continue; } @@ -299,7 +352,7 @@ public class UncachedKeyRing { continue; } - switch (zert.getSignatureType()) { + switch (type) { case PGPSignature.DEFAULT_CERTIFICATION: case PGPSignature.NO_CERTIFICATION: case PGPSignature.CASUAL_CERTIFICATION: @@ -356,14 +409,6 @@ public class UncachedKeyRing { } break; - default: - log.add(LogLevel.WARN, LogType.MSG_KC_UID_UNKNOWN_CERT, - new String[] { - "0x" + Integer.toString(zert.getSignatureType(), 16), - userId - }, indent); - modified = PGPPublicKey.removeCertification(modified, userId, zert); - removedCerts += 1; } } @@ -401,6 +446,7 @@ public class UncachedKeyRing { log.add(LogLevel.WARN, LogType.MSG_KC_SUB_BAD_KEYID, null, indent); continue; } + if (type != PGPSignature.SUBKEY_BINDING && type != PGPSignature.SUBKEY_REVOCATION) { log.add(LogLevel.WARN, LogType.MSG_KC_SUB_BAD_TYPE, new String[]{ "0x" + Integer.toString(type, 16) @@ -408,6 +454,18 @@ public class UncachedKeyRing { continue; } + if (cert.getCreationTime().after(now)) { + // Creation date in the future? No way! + log.add(LogLevel.WARN, LogType.MSG_KC_SUB_BAD_TIME, null, indent); + continue; + } + + if (cert.isLocal()) { + // Creation date in the future? No way! + log.add(LogLevel.WARN, LogType.MSG_KC_SUB_BAD_LOCAL, null, indent); + continue; + } + if (type == PGPSignature.SUBKEY_BINDING) { // TODO verify primary key binding signature for signing keys! diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java index 093b6c96f..be7f960a9 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java @@ -178,4 +178,11 @@ public class WrappedSignature { return new WrappedSignature(signatures.get(0)); } + public boolean isLocal() { + if (!mSig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.EXPORTABLE)) { + return false; + } + SignatureSubpacket p = mSig.getHashedSubPackets().getSubpacket(SignatureSubpacketTags.EXPORTABLE); + return p.getData()[0] == 0; + } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index f83dc796f..e7fb951cd 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -161,12 +161,17 @@ public class OperationResultParcel implements Parcelable { MSG_KC_MASTER (R.string.msg_kc_master), MSG_KC_MASTER_SUCCESS (R.string.msg_kc_master_success), MSG_KC_REVOKE_BAD_ERR (R.string.msg_kc_revoke_bad_err), + MSG_KC_REVOKE_BAD_LOCAL (R.string.msg_kc_revoke_bad_local), + MSG_KC_REVOKE_BAD_TIME (R.string.msg_kc_revoke_bad_time), + MSG_KC_REVOKE_BAD_TYPE (R.string.msg_kc_revoke_bad_type), MSG_KC_REVOKE_BAD (R.string.msg_kc_revoke_bad), MSG_KC_REVOKE_DUP (R.string.msg_kc_revoke_dup), MSG_KC_SUB (R.string.msg_kc_sub), + MSG_KC_SUB_BAD(R.string.msg_kc_sub_bad), MSG_KC_SUB_BAD_ERR(R.string.msg_kc_sub_bad_err), + MSG_KC_SUB_BAD_LOCAL(R.string.msg_kc_sub_bad_local), MSG_KC_SUB_BAD_KEYID(R.string.msg_kc_sub_bad_keyid), - MSG_KC_SUB_BAD(R.string.msg_kc_sub_bad), + MSG_KC_SUB_BAD_TIME(R.string.msg_kc_sub_bad_time), MSG_KC_SUB_BAD_TYPE(R.string.msg_kc_sub_bad_type), MSG_KC_SUB_NO_CERT(R.string.msg_kc_sub_no_cert), MSG_KC_SUB_REVOKE_BAD_ERR (R.string.msg_kc_sub_revoke_bad_err), @@ -176,11 +181,13 @@ public class OperationResultParcel implements Parcelable { MSG_KC_SUCCESS_REMOVED (R.string.msg_kc_success_removed), MSG_KC_SUCCESS (R.string.msg_kc_success), MSG_KC_UID_BAD_ERR (R.string.msg_kc_uid_bad_err), + MSG_KC_UID_BAD_LOCAL (R.string.msg_kc_uid_bad_local), + MSG_KC_UID_BAD_TIME (R.string.msg_kc_uid_bad_time), + MSG_KC_UID_BAD_TYPE (R.string.msg_kc_uid_bad_type), MSG_KC_UID_BAD (R.string.msg_kc_uid_bad), MSG_KC_UID_DUP (R.string.msg_kc_uid_dup), MSG_KC_UID_REVOKE_DUP (R.string.msg_kc_uid_revoke_dup), MSG_KC_UID_REVOKE_OLD (R.string.msg_kc_uid_revoke_old), - MSG_KC_UID_UNKNOWN_CERT (R.string.msg_kc_uid_unknown_cert), ; private final int mMsgId; -- cgit v1.2.3 From 79131be5f09c9ebaaa9c76ef717b2bd68c9b49db Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 12 Jun 2014 22:27:32 +0200 Subject: canonicalize: simplify import with info from canonicalization --- .../keychain/provider/ProviderHelper.java | 44 +++++----------------- .../keychain/service/OperationResultParcel.java | 4 -- 2 files changed, 9 insertions(+), 39 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') 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 96bf3f207..102c8e6d0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -177,7 +177,7 @@ public class ProviderHelper { return getGenericData(KeyRings.buildUnifiedKeyRingUri(masterKeyId), proj, types); } - private LongSparseArray getAllWrappedMasterKeys() { + private LongSparseArray getTrustedMasterKeys() { Cursor cursor = mContentResolver.query(KeyRings.buildUnifiedKeyRingsUri(), new String[] { KeyRings.MASTER_KEY_ID, // we pick from cache only information that is not easily available from keyrings @@ -361,12 +361,6 @@ public class ProviderHelper { Date creation = key.getCreationTime(); values.put(Keys.CREATION, creation.getTime() / 1000); - if (creation.after(new Date())) { - log(LogLevel.ERROR, LogType.MSG_IP_SUBKEY_FUTURE, new String[]{ - creation.toString() - }); - return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); - } Date expiryDate = key.getExpiryTime(); if (expiryDate != null) { values.put(Keys.EXPIRY, expiryDate.getTime() / 1000); @@ -389,7 +383,7 @@ public class ProviderHelper { mIndent -= 1; // get a list of owned secret keys, for verification filtering - LongSparseArray trustedKeys = getAllWrappedMasterKeys(); + LongSparseArray trustedKeys = getTrustedMasterKeys(); // classify and order user ids. primary are moved to the front, revoked to the back, // otherwise the order in the keyfile is preserved. @@ -415,34 +409,16 @@ public class ProviderHelper { try { // self signature if (certId == masterKeyId) { - cert.init(masterKey); - if (!cert.verifySignature(masterKey, userId)) { - // Bad self certification? That's kinda bad... - log(LogLevel.ERROR, LogType.MSG_IP_UID_SELF_BAD); - return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); - } - // if we already have a cert.. - if (item.selfCert != null) { - // ..is this perchance a more recent one? - if (item.selfCert.getCreationTime().before(cert.getCreationTime())) { - log(LogLevel.DEBUG, LogType.MSG_IP_UID_SELF_NEWER); - } else { - log(LogLevel.DEBUG, LogType.MSG_IP_UID_SELF_IGNORING_OLD); - continue; - } - } else { + // NOTE self-certificates are already verified during canonicalization, + // AND we know there is at most one cert plus at most one revocation + if (!cert.isRevocation()) { + item.selfCert = cert; + item.isPrimary = cert.isPrimaryUserId(); log(LogLevel.DEBUG, LogType.MSG_IP_UID_SELF_GOOD); - } - - // save certificate as primary self-cert - item.selfCert = cert; - item.isPrimary = cert.isPrimaryUserId(); - if (cert.isRevocation()) { + } else { item.isRevoked = true; log(LogLevel.DEBUG, LogType.MSG_IP_UID_REVOKED); - } else { - item.isRevoked = false; } } @@ -489,10 +465,8 @@ public class ProviderHelper { for (int userIdRank = 0; userIdRank < uids.size(); userIdRank++) { UserIdItem item = uids.get(userIdRank); operations.add(buildUserIdOperations(masterKeyId, item, userIdRank)); - // no self cert is bad, but allowed by the rfc... if (item.selfCert != null) { - operations.add(buildCertOperations( - masterKeyId, userIdRank, item.selfCert, + operations.add(buildCertOperations(masterKeyId, userIdRank, item.selfCert, secretRing != null ? Certs.VERIFIED_SECRET : Certs.VERIFIED_SELF)); } // don't bother with trusted certs if the uid is revoked, anyways diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index e7fb951cd..701285fa8 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -131,7 +131,6 @@ public class OperationResultParcel implements Parcelable { MSG_IP_SUBKEY_FLAGS_XEX (R.string.msg_ip_subkey_flags_xex), MSG_IP_SUBKEY_FLAGS_XXS (R.string.msg_ip_subkey_flags_xxs), MSG_IP_SUBKEY_FLAGS_XXX (R.string.msg_ip_subkey_flags_xxx), - MSG_IP_SUBKEY_FUTURE (R.string.msg_ip_subkey_future), MSG_IP_SUCCESS (R.string.msg_ip_success), MSG_IP_UID_CERT_BAD (R.string.msg_ip_uid_cert_bad), MSG_IP_UID_CERT_ERROR (R.string.msg_ip_uid_cert_error), @@ -141,10 +140,7 @@ public class OperationResultParcel implements Parcelable { MSG_IP_UID_REORDER(R.string.msg_ip_uid_reorder), MSG_IP_UID_PROCESSING (R.string.msg_ip_uid_processing), MSG_IP_UID_REVOKED (R.string.msg_ip_uid_revoked), - MSG_IP_UID_SELF_BAD (R.string.msg_ip_uid_self_bad), MSG_IP_UID_SELF_GOOD (R.string.msg_ip_uid_self_good), - MSG_IP_UID_SELF_IGNORING_OLD (R.string.msg_ip_uid_self_ignoring_old), - MSG_IP_UID_SELF_NEWER (R.string.msg_ip_uid_self_newer), // import secret MSG_IS(R.string.msg_is), -- cgit v1.2.3 From 073433fa747d698c6666081b2ee312062ecf2115 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 12 Jun 2014 23:10:44 +0200 Subject: canonicalize: require primary key binding certificates for signing subkeys --- .../keychain/pgp/UncachedKeyRing.java | 44 ++++++++++++++++++++-- .../keychain/service/OperationResultParcel.java | 3 ++ 2 files changed, 44 insertions(+), 3 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java index 8019b2b52..e309ed632 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java @@ -3,6 +3,8 @@ package org.sufficientlysecure.keychain.pgp; import org.spongycastle.bcpg.ArmoredOutputStream; import org.spongycastle.bcpg.S2K; import org.spongycastle.bcpg.SignatureSubpacketTags; +import org.spongycastle.bcpg.sig.KeyFlags; +import org.spongycastle.openpgp.PGPKeyFlags; import org.spongycastle.openpgp.PGPKeyRing; import org.spongycastle.openpgp.PGPObjectFactory; import org.spongycastle.openpgp.PGPPublicKey; @@ -10,6 +12,7 @@ import org.spongycastle.openpgp.PGPPublicKeyRing; import org.spongycastle.openpgp.PGPSecretKey; import org.spongycastle.openpgp.PGPSecretKeyRing; import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.PGPSignatureList; import org.spongycastle.openpgp.PGPUtil; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; @@ -298,7 +301,6 @@ public class UncachedKeyRing { WrappedSignature cert = new WrappedSignature(zert); long certId = cert.getKeyId(); - int type = zert.getSignatureType(); if (type != PGPSignature.DEFAULT_CERTIFICATION && type != PGPSignature.NO_CERTIFICATION @@ -435,9 +437,12 @@ public class UncachedKeyRing { // certificate. PGPPublicKey modified = key; PGPSignature selfCert = null, revocation = null; - for (PGPSignature zig : new IterableIterator(key.getSignatures())) { + uids: for (PGPSignature zig : new IterableIterator(key.getSignatures())) { // remove from keyring (for now) modified = PGPPublicKey.removeCertification(modified, zig); + // add this too, easier than adding it for every single "continue" case + removedCerts += 1; + WrappedSignature cert = new WrappedSignature(zig); int type = cert.getSignatureType(); @@ -467,7 +472,6 @@ public class UncachedKeyRing { } if (type == PGPSignature.SUBKEY_BINDING) { - // TODO verify primary key binding signature for signing keys! // make sure the certificate checks out try { @@ -481,10 +485,42 @@ public class UncachedKeyRing { continue; } + if (zig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.KEY_FLAGS)) { + int flags = ((KeyFlags) zig.getHashedSubPackets() + .getSubpacket(SignatureSubpacketTags.KEY_FLAGS)).getFlags(); + // If this subkey is allowed to sign data, + if ((flags & PGPKeyFlags.CAN_SIGN) == PGPKeyFlags.CAN_SIGN) { + try { + PGPSignatureList list = zig.getUnhashedSubPackets().getEmbeddedSignatures(); + boolean ok = false; + for (int i = 0; i < list.size(); i++) { + WrappedSignature subsig = new WrappedSignature(list.get(i)); + if (subsig.getSignatureType() == PGPSignature.PRIMARYKEY_BINDING) { + subsig.init(key); + if (subsig.verifySignature(masterKey, key)) { + ok = true; + } else { + log.add(LogLevel.WARN, LogType.MSG_KC_SUB_PRIMARY_BAD, null, indent); + continue uids; + } + } + } + if (!ok) { + log.add(LogLevel.WARN, LogType.MSG_KC_SUB_PRIMARY_NONE, null, indent); + continue; + } + } catch (Exception e) { + log.add(LogLevel.WARN, LogType.MSG_KC_SUB_PRIMARY_BAD_ERR, null, indent); + continue; + } + } + } + // if we already have a cert, and this one is not newer: skip it if (selfCert != null && selfCert.getCreationTime().before(cert.getCreationTime())) { continue; } + selfCert = zig; // if this is newer than a possibly existing revocation, drop that one if (revocation != null && selfCert.getCreationTime().after(revocation.getCreationTime())) { @@ -525,9 +561,11 @@ public class UncachedKeyRing { // re-add certification modified = PGPPublicKey.addCertification(modified, selfCert); + removedCerts -= 1; // add revocation, if any if (revocation != null) { modified = PGPPublicKey.addCertification(modified, revocation); + removedCerts -= 1; } // replace pubkey in keyring ring = PGPPublicKeyRing.insertPublicKey(ring, modified); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index 701285fa8..b5f01ce4d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -169,6 +169,9 @@ public class OperationResultParcel implements Parcelable { MSG_KC_SUB_BAD_KEYID(R.string.msg_kc_sub_bad_keyid), MSG_KC_SUB_BAD_TIME(R.string.msg_kc_sub_bad_time), MSG_KC_SUB_BAD_TYPE(R.string.msg_kc_sub_bad_type), + MSG_KC_SUB_PRIMARY_BAD(R.string.msg_kc_sub_primary_bad), + MSG_KC_SUB_PRIMARY_BAD_ERR(R.string.msg_kc_sub_primary_bad_err), + MSG_KC_SUB_PRIMARY_NONE(R.string.msg_kc_sub_primary_none), MSG_KC_SUB_NO_CERT(R.string.msg_kc_sub_no_cert), MSG_KC_SUB_REVOKE_BAD_ERR (R.string.msg_kc_sub_revoke_bad_err), MSG_KC_SUB_REVOKE_BAD (R.string.msg_kc_sub_revoke_bad), -- cgit v1.2.3 From 41545e5e52cd75b3408c127feeb43d4f2bd86e0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ha=C3=9F?= Date: Sun, 15 Jun 2014 19:59:41 +0200 Subject: Added notification wrapper for toast like notifys --- .../keychain/ui/adapter/Notify.java | 70 ++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/Notify.java (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/Notify.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/Notify.java new file mode 100644 index 000000000..5b8979235 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/Notify.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2012-2014 Dominik Schürmann + * Copyright (C) 2011 Senecaso + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.sufficientlysecure.keychain.ui.adapter; + +import android.app.Activity; +import android.content.res.Resources; + +import com.github.johnpersano.supertoasts.SuperCardToast; +import com.github.johnpersano.supertoasts.SuperToast; + +/** + * @author danielhass + * Notify wrapper which allows a more easy use of different notification libraries + */ +public class Notify { + + public static enum Style {OK, WARN, ERROR} + + /** + * Shows a simple in-layout notification with the CharSequence given as parameter + * @param activity + * @param text Text to show + * @param style Notification styling + */ + public static void showNotify(Activity activity, CharSequence text, Style style) { + + SuperCardToast st = new SuperCardToast(activity); + st.setText(text); + st.setDuration(SuperToast.Duration.MEDIUM); + switch (style){ + case OK: + st.setBackground(SuperToast.Background.GREEN); + break; + case WARN: + st.setBackground(SuperToast.Background.ORANGE); + break; + case ERROR: + st.setBackground(SuperToast.Background.RED); + break; + } + st.show(); + + } + + /** + * Shows a simple in-layout notification with the resource text from given id + * @param activity + * @param resId ResourceId of notification text + * @param style Notification styling + * @throws Resources.NotFoundException + */ + public static void showNotify(Activity activity, int resId, Style style) throws Resources.NotFoundException { + showNotify(activity, activity.getResources().getText(resId), style); + } +} \ No newline at end of file -- cgit v1.2.3 From e48d5e4474a19cb81fa1842f35c85906c6ce5132 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ha=C3=9F?= Date: Sun, 15 Jun 2014 20:03:40 +0200 Subject: Replaced AppMsg with notify wrapper call --- .../java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java index d953e2591..6805ae49a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java @@ -24,6 +24,7 @@ import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.os.Messenger; +import android.provider.SyncStateContract; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -39,6 +40,7 @@ import org.sufficientlysecure.keychain.helper.FileHelper; import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; +import org.sufficientlysecure.keychain.ui.adapter.Notify; import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.FileDialogFragment; import org.sufficientlysecure.keychain.util.Log; @@ -115,7 +117,8 @@ public class DecryptFileFragment extends DecryptFragment { } if (mInputFilename.equals("")) { - AppMsg.makeText(getActivity(), R.string.no_file_selected, AppMsg.STYLE_ALERT).show(); + //AppMsg.makeText(getActivity(), R.string.no_file_selected, AppMsg.STYLE_ALERT).show(); + Notify.showNotify(getActivity(), R.string.no_file_selected, Notify.Style.ERROR); return; } -- cgit v1.2.3 From eeff0c7150435c038e667f1a1af6bd3ba7354a24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ha=C3=9F?= Date: Sun, 15 Jun 2014 20:11:40 +0200 Subject: Removed unused import --- .../java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java | 1 - 1 file changed, 1 deletion(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java index 6805ae49a..f0bdabe93 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java @@ -24,7 +24,6 @@ import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.os.Messenger; -import android.provider.SyncStateContract; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -- cgit v1.2.3 From 5bd0b5011e2af23dc8b1054f65c9459bdeddcd8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ha=C3=9F?= Date: Mon, 16 Jun 2014 13:13:43 +0200 Subject: Moved Notify class to utils --- .../keychain/ui/DecryptFileFragment.java | 2 +- .../keychain/ui/adapter/Notify.java | 70 ---------------------- .../sufficientlysecure/keychain/util/Notify.java | 70 ++++++++++++++++++++++ 3 files changed, 71 insertions(+), 71 deletions(-) delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/Notify.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Notify.java (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java index f0bdabe93..7fdab7bdd 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java @@ -39,7 +39,7 @@ import org.sufficientlysecure.keychain.helper.FileHelper; import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; -import org.sufficientlysecure.keychain.ui.adapter.Notify; +import org.sufficientlysecure.keychain.util.Notify; import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.FileDialogFragment; import org.sufficientlysecure.keychain.util.Log; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/Notify.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/Notify.java deleted file mode 100644 index 5b8979235..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/Notify.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2012-2014 Dominik Schürmann - * Copyright (C) 2011 Senecaso - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.sufficientlysecure.keychain.ui.adapter; - -import android.app.Activity; -import android.content.res.Resources; - -import com.github.johnpersano.supertoasts.SuperCardToast; -import com.github.johnpersano.supertoasts.SuperToast; - -/** - * @author danielhass - * Notify wrapper which allows a more easy use of different notification libraries - */ -public class Notify { - - public static enum Style {OK, WARN, ERROR} - - /** - * Shows a simple in-layout notification with the CharSequence given as parameter - * @param activity - * @param text Text to show - * @param style Notification styling - */ - public static void showNotify(Activity activity, CharSequence text, Style style) { - - SuperCardToast st = new SuperCardToast(activity); - st.setText(text); - st.setDuration(SuperToast.Duration.MEDIUM); - switch (style){ - case OK: - st.setBackground(SuperToast.Background.GREEN); - break; - case WARN: - st.setBackground(SuperToast.Background.ORANGE); - break; - case ERROR: - st.setBackground(SuperToast.Background.RED); - break; - } - st.show(); - - } - - /** - * Shows a simple in-layout notification with the resource text from given id - * @param activity - * @param resId ResourceId of notification text - * @param style Notification styling - * @throws Resources.NotFoundException - */ - public static void showNotify(Activity activity, int resId, Style style) throws Resources.NotFoundException { - showNotify(activity, activity.getResources().getText(resId), style); - } -} \ No newline at end of file diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Notify.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Notify.java new file mode 100644 index 000000000..245a99be7 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Notify.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2012-2014 Dominik Schürmann + * Copyright (C) 2011 Senecaso + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.sufficientlysecure.keychain.util; + +import android.app.Activity; +import android.content.res.Resources; + +import com.github.johnpersano.supertoasts.SuperCardToast; +import com.github.johnpersano.supertoasts.SuperToast; + +/** + * @author danielhass + * Notify wrapper which allows a more easy use of different notification libraries + */ +public class Notify { + + public static enum Style {OK, WARN, ERROR} + + /** + * Shows a simple in-layout notification with the CharSequence given as parameter + * @param activity + * @param text Text to show + * @param style Notification styling + */ + public static void showNotify(Activity activity, CharSequence text, Style style) { + + SuperCardToast st = new SuperCardToast(activity); + st.setText(text); + st.setDuration(SuperToast.Duration.MEDIUM); + switch (style){ + case OK: + st.setBackground(SuperToast.Background.GREEN); + break; + case WARN: + st.setBackground(SuperToast.Background.ORANGE); + break; + case ERROR: + st.setBackground(SuperToast.Background.RED); + break; + } + st.show(); + + } + + /** + * Shows a simple in-layout notification with the resource text from given id + * @param activity + * @param resId ResourceId of notification text + * @param style Notification styling + * @throws Resources.NotFoundException + */ + public static void showNotify(Activity activity, int resId, Style style) throws Resources.NotFoundException { + showNotify(activity, activity.getResources().getText(resId), style); + } +} \ No newline at end of file -- cgit v1.2.3 From da28c000adf57baab1c282cc6efd3f1d8eb9e392 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ha=C3=9F?= Date: Mon, 16 Jun 2014 13:19:34 +0200 Subject: Changed license header --- .../sufficientlysecure/keychain/util/Notify.java | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Notify.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Notify.java index 245a99be7..67f81fb24 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Notify.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Notify.java @@ -1,18 +1,18 @@ /* - * Copyright (C) 2012-2014 Dominik Schürmann - * Copyright (C) 2011 Senecaso + * Copyright (C) 2014 Dominik Schürmann * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * 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. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . */ package org.sufficientlysecure.keychain.util; -- cgit v1.2.3 From 109bea754207e960a22eaea7df819273df51112f Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Mon, 16 Jun 2014 22:00:19 +0200 Subject: import-log: distinguish master/subkeys, distinguish redundant/bad certs, more docs --- .../keychain/pgp/UncachedKeyRing.java | 99 +++++++++++++--------- .../keychain/provider/ProviderHelper.java | 64 +++++++++----- .../keychain/service/OperationResultParcel.java | 40 +++++++-- 3 files changed, 133 insertions(+), 70 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java index e309ed632..9b9818c2f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java @@ -185,7 +185,12 @@ public class UncachedKeyRing { * - Remove all non-verifying self-certificates * - Remove all "future" self-certificates * - Remove all certificates flagged as "local" - * - Remove all certificates which are superseded by a newer one on the same target + * - Remove all certificates which are superseded by a newer one on the same target, + * including revocations with later re-certifications. + * - Remove all certificates of unknown type: + * - key revocation signatures on the master key + * - subkey binding signatures for subkeys + * - certifications and certification revocations for user ids * * After this cleaning, a number of checks are done: TODO implement * - See if each subkey retains a valid self certificate @@ -208,7 +213,7 @@ public class UncachedKeyRing { final Date now = new Date(); - int removedCerts = 0; + int redundantCerts = 0, badCerts = 0; PGPPublicKeyRing ring = (PGPPublicKeyRing) mRing; PGPPublicKey masterKey = mRing.getPublicKey(); @@ -240,7 +245,7 @@ public class UncachedKeyRing { "0x" + Integer.toString(type, 16) }, indent); modified = PGPPublicKey.removeCertification(modified, zert); - removedCerts += 1; + badCerts += 1; continue; } @@ -248,7 +253,7 @@ public class UncachedKeyRing { // Creation date in the future? No way! log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD_TIME, null, indent); modified = PGPPublicKey.removeCertification(modified, zert); - removedCerts += 1; + badCerts += 1; continue; } @@ -256,7 +261,7 @@ public class UncachedKeyRing { // Creation date in the future? No way! log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD_LOCAL, null, indent); modified = PGPPublicKey.removeCertification(modified, zert); - removedCerts += 1; + badCerts += 1; continue; } @@ -265,13 +270,13 @@ public class UncachedKeyRing { if (!cert.verifySignature(masterKey)) { log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD, null, indent); modified = PGPPublicKey.removeCertification(modified, zert); - removedCerts += 1; + badCerts += 1; continue; } } catch (PgpGeneralException e) { log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD_ERR, null, indent); modified = PGPPublicKey.removeCertification(modified, zert); - removedCerts += 1; + badCerts += 1; continue; } @@ -281,12 +286,12 @@ public class UncachedKeyRing { // more revocations? at least one is superfluous, then. } else if (revocation.getCreationTime().before(zert.getCreationTime())) { modified = PGPPublicKey.removeCertification(modified, revocation); - removedCerts += 1; + redundantCerts += 1; log.add(LogLevel.INFO, LogType.MSG_KC_REVOKE_DUP, null, indent); revocation = zert; } else { modified = PGPPublicKey.removeCertification(modified, zert); - removedCerts += 1; + redundantCerts += 1; log.add(LogLevel.INFO, LogType.MSG_KC_REVOKE_DUP, null, indent); } } @@ -312,14 +317,14 @@ public class UncachedKeyRing { "0x" + Integer.toString(zert.getSignatureType(), 16) }, indent); modified = PGPPublicKey.removeCertification(modified, userId, zert); - removedCerts += 1; + badCerts += 1; } if (cert.getCreationTime().after(now)) { // Creation date in the future? No way! log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD_TIME, null, indent); modified = PGPPublicKey.removeCertification(modified, zert); - removedCerts += 1; + badCerts += 1; continue; } @@ -327,7 +332,7 @@ public class UncachedKeyRing { // Creation date in the future? No way! log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD_LOCAL, null, indent); modified = PGPPublicKey.removeCertification(modified, zert); - removedCerts += 1; + badCerts += 1; continue; } @@ -343,14 +348,14 @@ public class UncachedKeyRing { log.add(LogLevel.WARN, LogType.MSG_KC_UID_BAD, new String[] { userId }, indent); modified = PGPPublicKey.removeCertification(modified, userId, zert); - removedCerts += 1; + badCerts += 1; continue; } } catch (PgpGeneralException e) { log.add(LogLevel.WARN, LogType.MSG_KC_UID_BAD_ERR, new String[] { userId }, indent); modified = PGPPublicKey.removeCertification(modified, userId, zert); - removedCerts += 1; + badCerts += 1; continue; } @@ -363,13 +368,13 @@ public class UncachedKeyRing { selfCert = zert; } else if (selfCert.getCreationTime().before(cert.getCreationTime())) { modified = PGPPublicKey.removeCertification(modified, userId, selfCert); - removedCerts += 1; + redundantCerts += 1; log.add(LogLevel.INFO, LogType.MSG_KC_UID_DUP, new String[] { userId }, indent); selfCert = zert; } else { modified = PGPPublicKey.removeCertification(modified, userId, zert); - removedCerts += 1; + redundantCerts += 1; log.add(LogLevel.INFO, LogType.MSG_KC_UID_DUP, new String[] { userId }, indent); } @@ -378,7 +383,7 @@ public class UncachedKeyRing { && revocation.getCreationTime().before(selfCert.getCreationTime())) { modified = PGPPublicKey.removeCertification(modified, userId, revocation); revocation = null; - removedCerts += 1; + redundantCerts += 1; log.add(LogLevel.INFO, LogType.MSG_KC_UID_REVOKE_OLD, new String[] { userId }, indent); } @@ -388,7 +393,7 @@ public class UncachedKeyRing { // If this is older than the (latest) self cert, drop it if (selfCert != null && selfCert.getCreationTime().after(zert.getCreationTime())) { modified = PGPPublicKey.removeCertification(modified, userId, zert); - removedCerts += 1; + redundantCerts += 1; log.add(LogLevel.INFO, LogType.MSG_KC_UID_REVOKE_OLD, new String[] { userId }, indent); continue; @@ -399,13 +404,13 @@ public class UncachedKeyRing { // more revocations? at least one is superfluous, then. } else if (revocation.getCreationTime().before(cert.getCreationTime())) { modified = PGPPublicKey.removeCertification(modified, userId, revocation); - removedCerts += 1; + redundantCerts += 1; log.add(LogLevel.INFO, LogType.MSG_KC_UID_REVOKE_DUP, new String[] { userId }, indent); revocation = zert; } else { modified = PGPPublicKey.removeCertification(modified, userId, zert); - removedCerts += 1; + redundantCerts += 1; log.add(LogLevel.INFO, LogType.MSG_KC_UID_REVOKE_DUP, new String[] { userId }, indent); } @@ -418,8 +423,6 @@ public class UncachedKeyRing { // Replace modified key in the keyring ring = PGPPublicKeyRing.insertPublicKey(ring, modified); - - log.add(LogLevel.DEBUG, LogType.MSG_KC_MASTER_SUCCESS, null, indent); indent -= 1; } @@ -437,18 +440,17 @@ public class UncachedKeyRing { // certificate. PGPPublicKey modified = key; PGPSignature selfCert = null, revocation = null; - uids: for (PGPSignature zig : new IterableIterator(key.getSignatures())) { + uids: for (PGPSignature zert : new IterableIterator(key.getSignatures())) { // remove from keyring (for now) - modified = PGPPublicKey.removeCertification(modified, zig); - // add this too, easier than adding it for every single "continue" case - removedCerts += 1; + modified = PGPPublicKey.removeCertification(modified, zert); - WrappedSignature cert = new WrappedSignature(zig); + WrappedSignature cert = new WrappedSignature(zert); int type = cert.getSignatureType(); // filter out bad key types... if (cert.getKeyId() != masterKey.getKeyID()) { log.add(LogLevel.WARN, LogType.MSG_KC_SUB_BAD_KEYID, null, indent); + badCerts += 1; continue; } @@ -456,18 +458,21 @@ public class UncachedKeyRing { log.add(LogLevel.WARN, LogType.MSG_KC_SUB_BAD_TYPE, new String[]{ "0x" + Integer.toString(type, 16) }, indent); + badCerts += 1; continue; } if (cert.getCreationTime().after(now)) { // Creation date in the future? No way! log.add(LogLevel.WARN, LogType.MSG_KC_SUB_BAD_TIME, null, indent); + badCerts += 1; continue; } if (cert.isLocal()) { // Creation date in the future? No way! log.add(LogLevel.WARN, LogType.MSG_KC_SUB_BAD_LOCAL, null, indent); + badCerts += 1; continue; } @@ -478,20 +483,22 @@ public class UncachedKeyRing { cert.init(masterKey); if (!cert.verifySignature(masterKey, key)) { log.add(LogLevel.WARN, LogType.MSG_KC_SUB_BAD, null, indent); + badCerts += 1; continue; } } catch (PgpGeneralException e) { log.add(LogLevel.WARN, LogType.MSG_KC_SUB_BAD_ERR, null, indent); + badCerts += 1; continue; } - if (zig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.KEY_FLAGS)) { - int flags = ((KeyFlags) zig.getHashedSubPackets() + if (zert.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.KEY_FLAGS)) { + int flags = ((KeyFlags) zert.getHashedSubPackets() .getSubpacket(SignatureSubpacketTags.KEY_FLAGS)).getFlags(); // If this subkey is allowed to sign data, if ((flags & PGPKeyFlags.CAN_SIGN) == PGPKeyFlags.CAN_SIGN) { try { - PGPSignatureList list = zig.getUnhashedSubPackets().getEmbeddedSignatures(); + PGPSignatureList list = zert.getUnhashedSubPackets().getEmbeddedSignatures(); boolean ok = false; for (int i = 0; i < list.size(); i++) { WrappedSignature subsig = new WrappedSignature(list.get(i)); @@ -501,16 +508,19 @@ public class UncachedKeyRing { ok = true; } else { log.add(LogLevel.WARN, LogType.MSG_KC_SUB_PRIMARY_BAD, null, indent); + badCerts += 1; continue uids; } } } if (!ok) { log.add(LogLevel.WARN, LogType.MSG_KC_SUB_PRIMARY_NONE, null, indent); + badCerts += 1; continue; } } catch (Exception e) { log.add(LogLevel.WARN, LogType.MSG_KC_SUB_PRIMARY_BAD_ERR, null, indent); + badCerts += 1; continue; } } @@ -518,10 +528,11 @@ public class UncachedKeyRing { // if we already have a cert, and this one is not newer: skip it if (selfCert != null && selfCert.getCreationTime().before(cert.getCreationTime())) { + redundantCerts += 1; continue; } - selfCert = zig; + selfCert = zert; // if this is newer than a possibly existing revocation, drop that one if (revocation != null && selfCert.getCreationTime().after(revocation.getCreationTime())) { revocation = null; @@ -535,17 +546,22 @@ public class UncachedKeyRing { cert.init(masterKey); if (!cert.verifySignature(key)) { log.add(LogLevel.WARN, LogType.MSG_KC_SUB_REVOKE_BAD, null, indent); + badCerts += 1; continue; } } catch (PgpGeneralException e) { log.add(LogLevel.WARN, LogType.MSG_KC_SUB_REVOKE_BAD_ERR, null, indent); + badCerts += 1; continue; } // if there is no binding (yet), or the revocation is newer than the binding: keep it - if (selfCert == null || selfCert.getCreationTime().before(cert.getCreationTime())) { - revocation = zig; + if (selfCert != null && selfCert.getCreationTime().after(cert.getCreationTime())) { + redundantCerts += 1; + continue; } + + revocation = zert; } } @@ -561,22 +577,25 @@ public class UncachedKeyRing { // re-add certification modified = PGPPublicKey.addCertification(modified, selfCert); - removedCerts -= 1; // add revocation, if any if (revocation != null) { modified = PGPPublicKey.addCertification(modified, revocation); - removedCerts -= 1; } // replace pubkey in keyring ring = PGPPublicKeyRing.insertPublicKey(ring, modified); - - log.add(LogLevel.DEBUG, LogType.MSG_KC_SUB_SUCCESS, null, indent); indent -= 1; } - if (removedCerts > 0) { - log.add(LogLevel.OK, LogType.MSG_KC_SUCCESS_REMOVED, - new String[] { Integer.toString(removedCerts) }, indent); + if (badCerts > 0 && redundantCerts > 0) { + log.add(LogLevel.OK, LogType.MSG_KC_SUCCESS_BAD_AND_RED, + new String[] { Integer.toString(badCerts), + Integer.toString(redundantCerts) }, indent); + } else if (badCerts > 0) { + log.add(LogLevel.OK, LogType.MSG_KC_SUCCESS_BAD, + new String[] { Integer.toString(badCerts) }, indent); + } else if (redundantCerts > 0) { + log.add(LogLevel.OK, LogType.MSG_KC_SUCCESS_REDUNDANT, + new String[] { Integer.toString(redundantCerts) }, indent); } else { log.add(LogLevel.OK, LogType.MSG_KC_SUCCESS, null, indent); } 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 102c8e6d0..80de65e0e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -302,7 +302,6 @@ public class ProviderHelper { log(LogLevel.INFO, LogType.MSG_IP_INSERT_KEYRING); { // insert keyring - // insert new version of this keyRing ContentValues values = new ContentValues(); values.put(KeyRingData.MASTER_KEY_ID, masterKeyId); try { @@ -322,8 +321,9 @@ public class ProviderHelper { Uri uri = Keys.buildKeysUri(Long.toString(masterKeyId)); int rank = 0; for (UncachedPublicKey key : new IterableIterator(keyRing.getPublicKeys())) { - log(LogLevel.DEBUG, LogType.MSG_IP_SUBKEY, new String[]{ - PgpKeyHelper.convertKeyIdToHex(key.getKeyId()) + long keyId = key.getKeyId(); + log(LogLevel.DEBUG, keyId == masterKeyId ? LogType.MSG_IP_MASTER : LogType.MSG_IP_SUBKEY, new String[]{ + PgpKeyHelper.convertKeyIdToHex(keyId) }); mIndent += 1; @@ -341,21 +341,41 @@ public class ProviderHelper { values.put(Keys.CAN_ENCRYPT, e); values.put(Keys.CAN_SIGN, s); values.put(Keys.IS_REVOKED, key.isRevoked()); - if (c) { - if (e) { - log(LogLevel.DEBUG, s ? LogType.MSG_IP_SUBKEY_FLAGS_CES - : LogType.MSG_IP_SUBKEY_FLAGS_CEX, null); + if (masterKeyId == keyId) { + if (c) { + if (e) { + log(LogLevel.DEBUG, s ? LogType.MSG_IP_MASTER_FLAGS_CES + : LogType.MSG_IP_MASTER_FLAGS_CEX, null); + } else { + log(LogLevel.DEBUG, s ? LogType.MSG_IP_MASTER_FLAGS_CXS + : LogType.MSG_IP_MASTER_FLAGS_CXX, null); + } } else { - log(LogLevel.DEBUG, s ? LogType.MSG_IP_SUBKEY_FLAGS_CXS - : LogType.MSG_IP_SUBKEY_FLAGS_CXX, null); + if (e) { + log(LogLevel.DEBUG, s ? LogType.MSG_IP_MASTER_FLAGS_XES + : LogType.MSG_IP_MASTER_FLAGS_XEX, null); + } else { + log(LogLevel.DEBUG, s ? LogType.MSG_IP_MASTER_FLAGS_XXS + : LogType.MSG_IP_MASTER_FLAGS_XXX, null); + } } } else { - if (e) { - log(LogLevel.DEBUG, s ? LogType.MSG_IP_SUBKEY_FLAGS_XES - : LogType.MSG_IP_SUBKEY_FLAGS_XEX, null); + if (c) { + if (e) { + log(LogLevel.DEBUG, s ? LogType.MSG_IP_SUBKEY_FLAGS_CES + : LogType.MSG_IP_SUBKEY_FLAGS_CEX, null); + } else { + log(LogLevel.DEBUG, s ? LogType.MSG_IP_SUBKEY_FLAGS_CXS + : LogType.MSG_IP_SUBKEY_FLAGS_CXX, null); + } } else { - log(LogLevel.DEBUG, s ? LogType.MSG_IP_SUBKEY_FLAGS_XXS - : LogType.MSG_IP_SUBKEY_FLAGS_XXX, null); + if (e) { + log(LogLevel.DEBUG, s ? LogType.MSG_IP_SUBKEY_FLAGS_XES + : LogType.MSG_IP_SUBKEY_FLAGS_XEX, null); + } else { + log(LogLevel.DEBUG, s ? LogType.MSG_IP_SUBKEY_FLAGS_XXS + : LogType.MSG_IP_SUBKEY_FLAGS_XXX, null); + } } } @@ -365,13 +385,13 @@ public class ProviderHelper { if (expiryDate != null) { values.put(Keys.EXPIRY, expiryDate.getTime() / 1000); if (key.isExpired()) { - log(LogLevel.DEBUG, LogType.MSG_IP_SUBKEY_EXPIRED, new String[]{ - expiryDate.toString() - }); + log(LogLevel.DEBUG, keyId == masterKeyId ? + LogType.MSG_IP_MASTER_EXPIRED : LogType.MSG_IP_SUBKEY_EXPIRED, + new String[]{ expiryDate.toString() }); } else { - log(LogLevel.DEBUG, LogType.MSG_IP_SUBKEY_EXPIRES, new String[]{ - expiryDate.toString() - }); + log(LogLevel.DEBUG, keyId == masterKeyId ? + LogType.MSG_IP_MASTER_EXPIRES : LogType.MSG_IP_SUBKEY_EXPIRES, + new String[] { expiryDate.toString() }); } } @@ -415,10 +435,9 @@ public class ProviderHelper { if (!cert.isRevocation()) { item.selfCert = cert; item.isPrimary = cert.isPrimaryUserId(); - log(LogLevel.DEBUG, LogType.MSG_IP_UID_SELF_GOOD); } else { item.isRevoked = true; - log(LogLevel.DEBUG, LogType.MSG_IP_UID_REVOKED); + log(LogLevel.INFO, LogType.MSG_IP_UID_REVOKED); } } @@ -479,7 +498,6 @@ public class ProviderHelper { } } - log(LogLevel.DEBUG, LogType.MSG_IP_PREPARE_SUCCESS); mIndent -= 1; } catch (IOException e) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index b5f01ce4d..b9531f83f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -16,6 +16,7 @@ import java.util.ArrayList; * list (ie, enum) of all possible log types, which should in all cases be tied * to string resource ids. * + * */ public class OperationResultParcel implements Parcelable { /** Holds the overall result, the number specifying varying degrees of success. The first bit @@ -101,6 +102,23 @@ public class OperationResultParcel implements Parcelable { } + /** This is an enum of all possible log events. + * + * Element names should generally be prefixed with MSG_XX_ where XX is an + * identifier based on the related activity. + * + * Log messages should occur for each distinguishable action group. For + * each such group, one message is displayed followed by warnings or + * errors, and optionally subactions. The granularity should generally be + * optimistic: No "success" messages are printed except for the outermost + * operations - the success of an action group is indicated by the + * beginning message of the next action group. + * + * Log messages should be in present tense, There should be no trailing + * punctuation, except for error messages which may end in an exclamation + * mark. + * + */ public static enum LogType { // import public @@ -114,15 +132,24 @@ public class OperationResultParcel implements Parcelable { MSG_IP_FAIL_OP_EX (R.string.msg_ip_fail_op_ex), MSG_IP_FAIL_REMOTE_EX (R.string.msg_ip_fail_remote_ex), MSG_IP_INSERT_KEYRING (R.string.msg_ip_insert_keyring), - MSG_IP_INSERT_SUBKEYS (R.string.msg_ip_insert_subkeys), + MSG_IP_INSERT_SUBKEYS (R.string.msg_ip_insert_keys), MSG_IP_PREPARE (R.string.msg_ip_prepare), - MSG_IP_PREPARE_SUCCESS(R.string.msg_ip_prepare_success), MSG_IP_PRESERVING_SECRET (R.string.msg_ip_preserving_secret), MSG_IP_REINSERT_SECRET (R.string.msg_ip_reinsert_secret), + MSG_IP_MASTER (R.string.msg_ip_master), + MSG_IP_MASTER_EXPIRED (R.string.msg_ip_master_expired), + MSG_IP_MASTER_EXPIRES (R.string.msg_ip_master_expires), + MSG_IP_MASTER_FLAGS_CES (R.string.msg_ip_master_flags_ces), + MSG_IP_MASTER_FLAGS_CEX (R.string.msg_ip_master_flags_cex), + MSG_IP_MASTER_FLAGS_CXS (R.string.msg_ip_master_flags_cxs), + MSG_IP_MASTER_FLAGS_XES (R.string.msg_ip_master_flags_xes), + MSG_IP_MASTER_FLAGS_CXX (R.string.msg_ip_master_flags_cxx), + MSG_IP_MASTER_FLAGS_XEX (R.string.msg_ip_master_flags_xex), + MSG_IP_MASTER_FLAGS_XXS (R.string.msg_ip_master_flags_xxs), + MSG_IP_MASTER_FLAGS_XXX (R.string.msg_ip_master_flags_xxx), MSG_IP_SUBKEY (R.string.msg_ip_subkey), MSG_IP_SUBKEY_EXPIRED (R.string.msg_ip_subkey_expired), MSG_IP_SUBKEY_EXPIRES (R.string.msg_ip_subkey_expires), - MSG_IP_SUBKEY_FLAGS (R.string.msg_ip_subkey_flags), MSG_IP_SUBKEY_FLAGS_CES (R.string.msg_ip_subkey_flags_ces), MSG_IP_SUBKEY_FLAGS_CEX (R.string.msg_ip_subkey_flags_cex), MSG_IP_SUBKEY_FLAGS_CXS (R.string.msg_ip_subkey_flags_cxs), @@ -140,7 +167,6 @@ public class OperationResultParcel implements Parcelable { MSG_IP_UID_REORDER(R.string.msg_ip_uid_reorder), MSG_IP_UID_PROCESSING (R.string.msg_ip_uid_processing), MSG_IP_UID_REVOKED (R.string.msg_ip_uid_revoked), - MSG_IP_UID_SELF_GOOD (R.string.msg_ip_uid_self_good), // import secret MSG_IS(R.string.msg_is), @@ -155,7 +181,6 @@ public class OperationResultParcel implements Parcelable { // keyring canonicalization MSG_KC (R.string.msg_kc), MSG_KC_MASTER (R.string.msg_kc_master), - MSG_KC_MASTER_SUCCESS (R.string.msg_kc_master_success), MSG_KC_REVOKE_BAD_ERR (R.string.msg_kc_revoke_bad_err), MSG_KC_REVOKE_BAD_LOCAL (R.string.msg_kc_revoke_bad_local), MSG_KC_REVOKE_BAD_TIME (R.string.msg_kc_revoke_bad_time), @@ -176,8 +201,9 @@ public class OperationResultParcel implements Parcelable { MSG_KC_SUB_REVOKE_BAD_ERR (R.string.msg_kc_sub_revoke_bad_err), MSG_KC_SUB_REVOKE_BAD (R.string.msg_kc_sub_revoke_bad), MSG_KC_SUB_REVOKE_DUP (R.string.msg_kc_sub_revoke_dup), - MSG_KC_SUB_SUCCESS (R.string.msg_kc_sub_success), - MSG_KC_SUCCESS_REMOVED (R.string.msg_kc_success_removed), + MSG_KC_SUCCESS_BAD (R.string.msg_kc_success_bad), + MSG_KC_SUCCESS_BAD_AND_RED (R.string.msg_kc_success_bad_and_red), + MSG_KC_SUCCESS_REDUNDANT (R.string.msg_kc_success_redundant), MSG_KC_SUCCESS (R.string.msg_kc_success), MSG_KC_UID_BAD_ERR (R.string.msg_kc_uid_bad_err), MSG_KC_UID_BAD_LOCAL (R.string.msg_kc_uid_bad_local), -- cgit v1.2.3 From 2f0e70587dfc21ce5c3d826f3d96b8231674f022 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Mon, 16 Jun 2014 16:23:15 +0200 Subject: add more progress info from import routine --- .../keychain/pgp/PgpImportExport.java | 6 +++-- .../keychain/provider/ProviderHelper.java | 27 +++++++++++++++++++++- .../keychain/ui/ViewKeyActivity.java | 13 +++++++++++ 3 files changed, 43 insertions(+), 3 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java index e1967429a..3681d62d8 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java @@ -37,6 +37,7 @@ import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLo import org.sufficientlysecure.keychain.service.OperationResults.ImportResult; import org.sufficientlysecure.keychain.service.OperationResults.SaveKeyringResult; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.ProgressScaler; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -130,6 +131,7 @@ public class PgpImportExport { int newKeys = 0, oldKeys = 0, badKeys = 0; int position = 0; + int progSteps = 100 / entries.size(); for (ParcelableKeyRing entry : entries) { try { UncachedKeyRing key = UncachedKeyRing.decodeFromData(entry.getBytes()); @@ -149,7 +151,8 @@ public class PgpImportExport { if (key.isSecret()) { result = mProviderHelper.saveSecretKeyRing(key); } else { - result = mProviderHelper.savePublicKeyRing(key); + result = mProviderHelper.savePublicKeyRing(key, + new ProgressScaler(mProgressable, position, (position+1)*progSteps, 100)); } if (!result.success()) { badKeys += 1; @@ -165,7 +168,6 @@ public class PgpImportExport { } // update progress position++; - updateProgress(position / entries.size() * 100, 100); } OperationLog log = mProviderHelper.getLog(); 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 80de65e0e..497957e5e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -29,6 +29,7 @@ import android.support.v4.util.LongSparseArray; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.pgp.KeyRing; +import org.sufficientlysecure.keychain.pgp.Progressable; import org.sufficientlysecure.keychain.pgp.WrappedPublicKey; import org.sufficientlysecure.keychain.service.OperationResultParcel.LogType; import org.sufficientlysecure.keychain.service.OperationResultParcel.LogLevel; @@ -259,11 +260,29 @@ public class ProviderHelper { } } + public SaveKeyringResult savePublicKeyRing(UncachedKeyRing keyRing) { + return savePublicKeyRing(keyRing, new Progressable() { + @Override + public void setProgress(String message, int current, int total) { + return; + } + + @Override + public void setProgress(int resourceId, int current, int total) { + return; + } + + @Override + public void setProgress(int current, int total) { + return; + } + }); + } /** * Saves PGPPublicKeyRing with its keys and userIds in DB */ @SuppressWarnings("unchecked") - public SaveKeyringResult savePublicKeyRing(UncachedKeyRing keyRing) { + public SaveKeyringResult savePublicKeyRing(UncachedKeyRing keyRing, Progressable progress) { if (keyRing.isSecret()) { log(LogLevel.ERROR, LogType.MSG_IP_BAD_TYPE_SECRET); return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); @@ -287,6 +306,7 @@ public class ProviderHelper { try { secretRing = getWrappedSecretKeyRing(masterKeyId).getUncached(); log(LogLevel.DEBUG, LogType.MSG_IP_PRESERVING_SECRET); + progress.setProgress(LogType.MSG_IP_PRESERVING_SECRET.getMsgId(), 30, 100); } catch (NotFoundException e) { secretRing = null; } @@ -316,6 +336,7 @@ public class ProviderHelper { } log(LogLevel.INFO, LogType.MSG_IP_INSERT_SUBKEYS); + progress.setProgress(LogType.MSG_IP_INSERT_SUBKEYS.getMsgId(), 40, 100); mIndent += 1; { // insert subkeys Uri uri = Keys.buildKeysUri(Long.toString(masterKeyId)); @@ -410,6 +431,7 @@ public class ProviderHelper { log(LogLevel.INFO, LogType.MSG_IP_UID_CLASSIFYING, new String[]{ Integer.toString(trustedKeys.size()) }); + progress.setProgress(LogType.MSG_IP_UID_CLASSIFYING.getMsgId(), 60, 100); mIndent += 1; List uids = new ArrayList(); for (String userId : new IterableIterator( @@ -476,6 +498,7 @@ public class ProviderHelper { } mIndent -= 1; + progress.setProgress(LogType.MSG_IP_UID_REORDER.getMsgId(), 80, 100); log(LogLevel.DEBUG, LogType.MSG_IP_UID_REORDER); // primary before regular before revoked (see UserIdItem.compareTo) // this is a stable sort, so the order of keys is otherwise preserved. @@ -519,6 +542,7 @@ public class ProviderHelper { } log(LogLevel.DEBUG, LogType.MSG_IP_APPLY_BATCH); + progress.setProgress(LogType.MSG_IP_APPLY_BATCH.getMsgId(), 90, 100); mContentResolver.applyBatch(KeychainContract.CONTENT_AUTHORITY, operations); // Save the saved keyring (if any) @@ -532,6 +556,7 @@ public class ProviderHelper { mIndent -= 1; log(LogLevel.OK, LogType.MSG_IP_SUCCESS); + progress.setProgress(LogType.MSG_IP_SUCCESS.getMsgId(), 100, 100); return new SaveKeyringResult(result, mLog); } catch (RemoteException e) { 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 463c800d2..13e9d22ab 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -56,6 +56,7 @@ import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter; +import org.sufficientlysecure.keychain.ui.widget.SlidingTabLayout.TabColorizer; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.ui.widget.SlidingTabLayout; @@ -121,6 +122,18 @@ public class ViewKeyActivity extends ActionBarActivity implements mViewPager = (ViewPager) findViewById(R.id.view_key_pager); mSlidingTabLayout = (SlidingTabLayout) findViewById(R.id.view_key_sliding_tab_layout); + mSlidingTabLayout.setCustomTabColorizer(new TabColorizer() { + @Override + public int getIndicatorColor(int position) { + return position == TAB_CERTS || position == TAB_KEYS ? 0xFFFF4444 : 0xFFAA66CC; + } + + @Override + public int getDividerColor(int position) { + return 0; + } + }); + int switchToTab = TAB_MAIN; Intent intent = getIntent(); if (intent.getExtras() != null && intent.getExtras().containsKey(EXTRA_SELECTED_TAB)) { -- cgit v1.2.3 From ffea551afc95ec68892ca9003f99c383c7cc5661 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Mon, 16 Jun 2014 22:32:17 +0200 Subject: drop invalid user ids and add fatal case(s) --- .../keychain/pgp/UncachedKeyRing.java | 21 ++++++++++++++++----- .../keychain/provider/ProviderHelper.java | 3 +++ .../keychain/service/OperationResultParcel.java | 2 ++ 3 files changed, 21 insertions(+), 5 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java index 9b9818c2f..26c6c6451 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java @@ -191,10 +191,8 @@ public class UncachedKeyRing { * - key revocation signatures on the master key * - subkey binding signatures for subkeys * - certifications and certification revocations for user ids - * - * After this cleaning, a number of checks are done: TODO implement - * - See if each subkey retains a valid self certificate - * - See if each user id retains a valid self certificate + * - If a subkey retains no valid subkey binding certificate, remove it + * - If a user id retains no valid self certificate, remove it * * This operation writes an OperationLog which can be used as part of a OperationResultParcel. * @@ -419,6 +417,19 @@ public class UncachedKeyRing { } } + + // If no valid certificate (if only a revocation) remains, drop it + if (selfCert == null && revocation == null) { + modified = PGPPublicKey.removeCertification(modified, userId); + log.add(LogLevel.ERROR, LogType.MSG_KC_UID_REVOKE_DUP, + new String[] { userId }, indent); + } + } + + // If NO user ids remain, error out! + if (!modified.getUserIDs().hasNext()) { + log.add(LogLevel.ERROR, LogType.MSG_KC_FATAL_NO_UID, null, indent); + return null; } // Replace modified key in the keyring @@ -570,7 +581,7 @@ public class UncachedKeyRing { ring = PGPPublicKeyRing.removePublicKey(ring, modified); log.add(LogLevel.ERROR, LogType.MSG_KC_SUB_NO_CERT, - new String[]{PgpKeyHelper.convertKeyIdToHex(key.getKeyID())}, indent); + new String[]{ PgpKeyHelper.convertKeyIdToHex(key.getKeyID()) }, indent); indent -= 1; continue; } 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 497957e5e..80fabdf48 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -298,6 +298,9 @@ public class ProviderHelper { // Canonicalize this key, to assert a number of assumptions made about it. keyRing = keyRing.canonicalize(mLog, mIndent); + if (keyRing == null) { + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + } UncachedPublicKey masterKey = keyRing.getPublicKey(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index b9531f83f..0760aadf8 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -180,6 +180,7 @@ public class OperationResultParcel implements Parcelable { // keyring canonicalization MSG_KC (R.string.msg_kc), + MSG_KC_FATAL_NO_UID (R.string.msg_kc_fatal_no_uid), MSG_KC_MASTER (R.string.msg_kc_master), MSG_KC_REVOKE_BAD_ERR (R.string.msg_kc_revoke_bad_err), MSG_KC_REVOKE_BAD_LOCAL (R.string.msg_kc_revoke_bad_local), @@ -211,6 +212,7 @@ public class OperationResultParcel implements Parcelable { MSG_KC_UID_BAD_TYPE (R.string.msg_kc_uid_bad_type), MSG_KC_UID_BAD (R.string.msg_kc_uid_bad), MSG_KC_UID_DUP (R.string.msg_kc_uid_dup), + MSG_KC_UID_NO_CERT (R.string.msg_kc_uid_no_cert), MSG_KC_UID_REVOKE_DUP (R.string.msg_kc_uid_revoke_dup), MSG_KC_UID_REVOKE_OLD (R.string.msg_kc_uid_revoke_old), ; -- cgit v1.2.3 From 9058291c0504c0684ef9d84edbc2c4e7c446d6d2 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 17 Jun 2014 11:57:14 +0200 Subject: canonicalize: fix NullPointerException --- .../java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java index be7f960a9..196ac1dee 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java @@ -179,7 +179,8 @@ public class WrappedSignature { } public boolean isLocal() { - if (!mSig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.EXPORTABLE)) { + if (!mSig.hasSubpackets() + || !mSig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.EXPORTABLE)) { return false; } SignatureSubpacket p = mSig.getHashedSubPackets().getSubpacket(SignatureSubpacketTags.EXPORTABLE); -- cgit v1.2.3 From 02fbaaf8580c4988d9e4e5031586ad4328ee21c2 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 17 Jun 2014 12:18:41 +0200 Subject: import-log: clean up progress messages --- .../org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java | 12 ++++++------ .../sufficientlysecure/keychain/provider/ProviderHelper.java | 1 - 2 files changed, 6 insertions(+), 7 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java index 26c6c6451..f22ea7697 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java @@ -367,13 +367,13 @@ public class UncachedKeyRing { } else if (selfCert.getCreationTime().before(cert.getCreationTime())) { modified = PGPPublicKey.removeCertification(modified, userId, selfCert); redundantCerts += 1; - log.add(LogLevel.INFO, LogType.MSG_KC_UID_DUP, + log.add(LogLevel.DEBUG, LogType.MSG_KC_UID_DUP, new String[] { userId }, indent); selfCert = zert; } else { modified = PGPPublicKey.removeCertification(modified, userId, zert); redundantCerts += 1; - log.add(LogLevel.INFO, LogType.MSG_KC_UID_DUP, + log.add(LogLevel.DEBUG, LogType.MSG_KC_UID_DUP, new String[] { userId }, indent); } // If there is a revocation certificate, and it's older than this, drop it @@ -382,7 +382,7 @@ public class UncachedKeyRing { modified = PGPPublicKey.removeCertification(modified, userId, revocation); revocation = null; redundantCerts += 1; - log.add(LogLevel.INFO, LogType.MSG_KC_UID_REVOKE_OLD, + log.add(LogLevel.DEBUG, LogType.MSG_KC_UID_REVOKE_OLD, new String[] { userId }, indent); } break; @@ -392,7 +392,7 @@ public class UncachedKeyRing { if (selfCert != null && selfCert.getCreationTime().after(zert.getCreationTime())) { modified = PGPPublicKey.removeCertification(modified, userId, zert); redundantCerts += 1; - log.add(LogLevel.INFO, LogType.MSG_KC_UID_REVOKE_OLD, + log.add(LogLevel.DEBUG, LogType.MSG_KC_UID_REVOKE_OLD, new String[] { userId }, indent); continue; } @@ -403,13 +403,13 @@ public class UncachedKeyRing { } else if (revocation.getCreationTime().before(cert.getCreationTime())) { modified = PGPPublicKey.removeCertification(modified, userId, revocation); redundantCerts += 1; - log.add(LogLevel.INFO, LogType.MSG_KC_UID_REVOKE_DUP, + log.add(LogLevel.DEBUG, LogType.MSG_KC_UID_REVOKE_DUP, new String[] { userId }, indent); revocation = zert; } else { modified = PGPPublicKey.removeCertification(modified, userId, zert); redundantCerts += 1; - log.add(LogLevel.INFO, LogType.MSG_KC_UID_REVOKE_DUP, + log.add(LogLevel.DEBUG, LogType.MSG_KC_UID_REVOKE_DUP, new String[] { userId }, indent); } break; 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 80fabdf48..8688ecb6c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -434,7 +434,6 @@ public class ProviderHelper { log(LogLevel.INFO, LogType.MSG_IP_UID_CLASSIFYING, new String[]{ Integer.toString(trustedKeys.size()) }); - progress.setProgress(LogType.MSG_IP_UID_CLASSIFYING.getMsgId(), 60, 100); mIndent += 1; List uids = new ArrayList(); for (String userId : new IterableIterator( -- cgit v1.2.3 From a0f546739d518a924770540795bca71bfacd4b1d Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 17 Jun 2014 20:03:58 +0200 Subject: new-edit: remove old save keyring stuff --- .../keychain/pgp/PgpKeyOperation.java | 599 ++------------------- .../keychain/service/KeychainIntentService.java | 156 +----- .../keychain/service/OldSaveKeyringParcel.java | 128 ----- .../keychain/service/SaveKeyringParcel.java | 2 +- .../keychain/ui/EditKeyActivity.java | 9 +- .../keychain/ui/widget/SectionView.java | 8 +- 6 files changed, 83 insertions(+), 819 deletions(-) delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OldSaveKeyringParcel.java (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') 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 44fc4c8c9..e9d8fd295 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java @@ -26,10 +26,8 @@ import org.spongycastle.jce.spec.ElGamalParameterSpec; import org.spongycastle.openpgp.PGPEncryptedData; import org.spongycastle.openpgp.PGPException; import org.spongycastle.openpgp.PGPKeyPair; -import org.spongycastle.openpgp.PGPKeyRingGenerator; import org.spongycastle.openpgp.PGPPrivateKey; import org.spongycastle.openpgp.PGPPublicKey; -import org.spongycastle.openpgp.PGPPublicKeyRing; import org.spongycastle.openpgp.PGPSecretKey; import org.spongycastle.openpgp.PGPSecretKeyRing; import org.spongycastle.openpgp.PGPSignature; @@ -49,7 +47,6 @@ import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException; -import org.sufficientlysecure.keychain.service.OldSaveKeyringParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.Primes; @@ -62,11 +59,9 @@ import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.SecureRandom; import java.security.SignatureException; -import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Date; -import java.util.Iterator; import java.util.List; import java.util.TimeZone; @@ -109,569 +104,81 @@ public class PgpKeyOperation { } } - /** - * Creates new secret key. - * - * @param algorithmChoice - * @param keySize - * @param passphrase - * @param isMasterKey - * @return A newly created PGPSecretKey - * @throws NoSuchAlgorithmException - * @throws PGPException - * @throws NoSuchProviderException - * @throws PgpGeneralMsgIdException - * @throws InvalidAlgorithmParameterException - */ - - // TODO: key flags? - public byte[] createKey(int algorithmChoice, int keySize, String passphrase, - boolean isMasterKey) - throws NoSuchAlgorithmException, PGPException, NoSuchProviderException, - PgpGeneralMsgIdException, InvalidAlgorithmParameterException { - - if (keySize < 512) { - throw new PgpGeneralMsgIdException(R.string.error_key_size_minimum512bit); - } - - if (passphrase == null) { - passphrase = ""; - } - - int algorithm; - KeyPairGenerator keyGen; - - switch (algorithmChoice) { - case Constants.choice.algorithm.dsa: { - keyGen = KeyPairGenerator.getInstance("DSA", Constants.BOUNCY_CASTLE_PROVIDER_NAME); - keyGen.initialize(keySize, new SecureRandom()); - algorithm = PGPPublicKey.DSA; - break; - } - - case Constants.choice.algorithm.elgamal: { - if (isMasterKey) { - throw new PgpGeneralMsgIdException(R.string.error_master_key_must_not_be_el_gamal); - } - keyGen = KeyPairGenerator.getInstance("ElGamal", Constants.BOUNCY_CASTLE_PROVIDER_NAME); - BigInteger p = Primes.getBestPrime(keySize); - BigInteger g = new BigInteger("2"); - - ElGamalParameterSpec elParams = new ElGamalParameterSpec(p, g); - - keyGen.initialize(elParams); - algorithm = PGPPublicKey.ELGAMAL_ENCRYPT; - break; - } - - case Constants.choice.algorithm.rsa: { - keyGen = KeyPairGenerator.getInstance("RSA", Constants.BOUNCY_CASTLE_PROVIDER_NAME); - keyGen.initialize(keySize, new SecureRandom()); - - algorithm = PGPPublicKey.RSA_GENERAL; - break; - } - - default: { - throw new PgpGeneralMsgIdException(R.string.error_unknown_algorithm_choice); - } - } - - // build new key pair - PGPKeyPair keyPair = new JcaPGPKeyPair(algorithm, keyGen.generateKeyPair(), new Date()); - - // define hashing and signing algos - PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build().get( - HashAlgorithmTags.SHA1); - - // Build key encrypter and decrypter based on passphrase - PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder( - PGPEncryptedData.CAST5, sha1Calc) - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); + /** Creates new secret key. */ + private PGPSecretKey createKey(int algorithmChoice, int keySize, String passphrase, + boolean isMasterKey) throws PgpGeneralMsgIdException { try { - return new PGPSecretKey(keyPair.getPrivateKey(), keyPair.getPublicKey(), - sha1Calc, isMasterKey, keyEncryptor).getEncoded(); - } catch(IOException e) { - throw new PgpGeneralMsgIdException(R.string.error_encoding); - } - } - - public Pair buildNewSecretKey( - OldSaveKeyringParcel saveParcel) - throws PgpGeneralMsgIdException, PGPException, SignatureException, IOException { - - int usageId = saveParcel.keysUsages.get(0); - boolean canSign; - String mainUserId = saveParcel.userIds.get(0); - - PGPSecretKey masterKey = saveParcel.keys.get(0).getSecretKeyExternal(); - - // this removes all userIds and certifications previously attached to the masterPublicKey - PGPPublicKey masterPublicKey = masterKey.getPublicKey(); - - PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( - Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(saveParcel.oldPassphrase.toCharArray()); - PGPPrivateKey masterPrivateKey = masterKey.extractPrivateKey(keyDecryptor); - - updateProgress(R.string.progress_certifying_master_key, 20, 100); - - for (String userId : saveParcel.userIds) { - PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( - masterPublicKey.getAlgorithm(), HashAlgorithmTags.SHA1) - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); - PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder); - - sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey); - - PGPSignature certification = sGen.generateCertification(userId, masterPublicKey); - masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, certification); - } - - PGPKeyPair masterKeyPair = new PGPKeyPair(masterPublicKey, masterPrivateKey); - - PGPSignatureSubpacketGenerator hashedPacketsGen = new PGPSignatureSubpacketGenerator(); - PGPSignatureSubpacketGenerator unhashedPacketsGen = new PGPSignatureSubpacketGenerator(); - - hashedPacketsGen.setKeyFlags(true, usageId); - - hashedPacketsGen.setPreferredSymmetricAlgorithms(true, PREFERRED_SYMMETRIC_ALGORITHMS); - hashedPacketsGen.setPreferredHashAlgorithms(true, PREFERRED_HASH_ALGORITHMS); - hashedPacketsGen.setPreferredCompressionAlgorithms(true, PREFERRED_COMPRESSION_ALGORITHMS); - - if (saveParcel.keysExpiryDates.get(0) != null) { - Calendar creationDate = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - creationDate.setTime(masterPublicKey.getCreationTime()); - Calendar expiryDate = saveParcel.keysExpiryDates.get(0); - //note that the below, (a/c) - (b/c) is *not* the same as (a - b) /c - //here we purposefully ignore partial days in each date - long type has no fractional part! - long numDays = (expiryDate.getTimeInMillis() / 86400000) - - (creationDate.getTimeInMillis() / 86400000); - if (numDays <= 0) { - throw new PgpGeneralMsgIdException(R.string.error_expiry_must_come_after_creation); + if (keySize < 512) { + throw new PgpGeneralMsgIdException(R.string.error_key_size_minimum512bit); } - hashedPacketsGen.setKeyExpirationTime(false, numDays * 86400); - } else { - hashedPacketsGen.setKeyExpirationTime(false, 0); - // do this explicitly, although since we're rebuilding, - // this happens anyway - } - updateProgress(R.string.progress_building_master_key, 30, 100); - - // define hashing and signing algos - PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build().get( - HashAlgorithmTags.SHA1); - PGPContentSignerBuilder certificationSignerBuilder = new JcaPGPContentSignerBuilder( - masterKeyPair.getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA1); - - // Build key encrypter based on passphrase - PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder( - PGPEncryptedData.CAST5, sha1Calc) - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( - saveParcel.newPassphrase.toCharArray()); - - PGPKeyRingGenerator keyGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, - masterKeyPair, mainUserId, sha1Calc, hashedPacketsGen.generate(), - unhashedPacketsGen.generate(), certificationSignerBuilder, keyEncryptor); - - updateProgress(R.string.progress_adding_sub_keys, 40, 100); - - for (int i = 1; i < saveParcel.keys.size(); ++i) { - updateProgress(40 + 40 * (i - 1) / (saveParcel.keys.size() - 1), 100); - - PGPSecretKey subKey = saveParcel.keys.get(i).getSecretKeyExternal(); - PGPPublicKey subPublicKey = subKey.getPublicKey(); - - PBESecretKeyDecryptor keyDecryptor2 = new JcePBESecretKeyDecryptorBuilder() - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( - saveParcel.oldPassphrase.toCharArray()); - PGPPrivateKey subPrivateKey = subKey.extractPrivateKey(keyDecryptor2); - - // TODO: now used without algorithm and creation time?! (APG 1) - PGPKeyPair subKeyPair = new PGPKeyPair(subPublicKey, subPrivateKey); - - hashedPacketsGen = new PGPSignatureSubpacketGenerator(); - unhashedPacketsGen = new PGPSignatureSubpacketGenerator(); - - usageId = saveParcel.keysUsages.get(i); - canSign = (usageId & KeyFlags.SIGN_DATA) > 0; //todo - separate function for this - if (canSign) { - Date todayDate = new Date(); //both sig times the same - // cross-certify signing keys - hashedPacketsGen.setSignatureCreationTime(false, todayDate); //set outer creation time - PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator(); - subHashedPacketsGen.setSignatureCreationTime(false, todayDate); //set inner creation time - PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( - subPublicKey.getAlgorithm(), PGPUtil.SHA1) - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); - PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder); - sGen.init(PGPSignature.PRIMARYKEY_BINDING, subPrivateKey); - sGen.setHashedSubpackets(subHashedPacketsGen.generate()); - PGPSignature certification = sGen.generateCertification(masterPublicKey, - subPublicKey); - unhashedPacketsGen.setEmbeddedSignature(false, certification); - } - hashedPacketsGen.setKeyFlags(false, usageId); - - if (saveParcel.keysExpiryDates.get(i) != null) { - Calendar creationDate = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - creationDate.setTime(subPublicKey.getCreationTime()); - Calendar expiryDate = saveParcel.keysExpiryDates.get(i); - //note that the below, (a/c) - (b/c) is *not* the same as (a - b) /c - //here we purposefully ignore partial days in each date - long type has no fractional part! - long numDays = (expiryDate.getTimeInMillis() / 86400000) - - (creationDate.getTimeInMillis() / 86400000); - if (numDays <= 0) { - throw new PgpGeneralMsgIdException(R.string.error_expiry_must_come_after_creation); - } - hashedPacketsGen.setKeyExpirationTime(false, numDays * 86400); - } else { - hashedPacketsGen.setKeyExpirationTime(false, 0); - // do this explicitly, although since we're rebuilding, - // this happens anyway + if (passphrase == null) { + passphrase = ""; } - keyGen.addSubKey(subKeyPair, hashedPacketsGen.generate(), unhashedPacketsGen.generate()); - } - - PGPSecretKeyRing secretKeyRing = keyGen.generateSecretKeyRing(); - PGPPublicKeyRing publicKeyRing = keyGen.generatePublicKeyRing(); + int algorithm; + KeyPairGenerator keyGen; - return new Pair(new UncachedKeyRing(secretKeyRing), new UncachedKeyRing(publicKeyRing)); - - } - - public Pair buildSecretKey(WrappedSecretKeyRing wmKR, - WrappedPublicKeyRing wpKR, - OldSaveKeyringParcel saveParcel) - throws PgpGeneralMsgIdException, PGPException, SignatureException, IOException { - - PGPSecretKeyRing mKR = wmKR.getRing(); - PGPPublicKeyRing pKR = wpKR.getRing(); - - updateProgress(R.string.progress_building_key, 0, 100); - - if (saveParcel.oldPassphrase == null) { - saveParcel.oldPassphrase = ""; - } - if (saveParcel.newPassphrase == null) { - saveParcel.newPassphrase = ""; - } - - /* - IDs - NB This might not need to happen later, if we change the way the primary ID is chosen - remove deleted ids - if the primary ID changed we need to: - remove all of the IDs from the keyring, saving their certifications - add them all in again, updating certs of IDs which have changed - else - remove changed IDs and add in with new certs - - if the master key changed, we need to remove the primary ID certification, so we can add - the new one when it is generated, and they don't conflict - - Keys - remove deleted keys - if a key is modified, re-sign it - do we need to remove and add in? - - Todo - identify more things which need to be preserved - e.g. trust levels? - user attributes - */ - - if (saveParcel.deletedKeys != null) { - for (UncachedSecretKey dKey : saveParcel.deletedKeys) { - mKR = PGPSecretKeyRing.removeSecretKey(mKR, dKey.getSecretKeyExternal()); - } - } - - PGPSecretKey masterKey = mKR.getSecretKey(); - PGPPublicKey masterPublicKey = masterKey.getPublicKey(); - - int usageId = saveParcel.keysUsages.get(0); - boolean canSign; - String mainUserId = saveParcel.userIds.get(0); - - PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( - Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(saveParcel.oldPassphrase.toCharArray()); - PGPPrivateKey masterPrivateKey = masterKey.extractPrivateKey(keyDecryptor); - - updateProgress(R.string.progress_certifying_master_key, 20, 100); - - boolean anyIDChanged = false; - for (String delID : saveParcel.deletedIDs) { - anyIDChanged = true; - masterPublicKey = PGPPublicKey.removeCertification(masterPublicKey, delID); - } - - int userIDIndex = 0; - - PGPSignatureSubpacketGenerator hashedPacketsGen = new PGPSignatureSubpacketGenerator(); - PGPSignatureSubpacketGenerator unhashedPacketsGen = new PGPSignatureSubpacketGenerator(); - - hashedPacketsGen.setKeyFlags(true, usageId); - - hashedPacketsGen.setPreferredSymmetricAlgorithms(true, PREFERRED_SYMMETRIC_ALGORITHMS); - hashedPacketsGen.setPreferredHashAlgorithms(true, PREFERRED_HASH_ALGORITHMS); - hashedPacketsGen.setPreferredCompressionAlgorithms(true, PREFERRED_COMPRESSION_ALGORITHMS); - - if (saveParcel.keysExpiryDates.get(0) != null) { - Calendar creationDate = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - creationDate.setTime(masterPublicKey.getCreationTime()); - Calendar expiryDate = saveParcel.keysExpiryDates.get(0); - //note that the below, (a/c) - (b/c) is *not* the same as (a - b) /c - //here we purposefully ignore partial days in each date - long type has no fractional part! - long numDays = (expiryDate.getTimeInMillis() / 86400000) - - (creationDate.getTimeInMillis() / 86400000); - if (numDays <= 0) { - throw new PgpGeneralMsgIdException(R.string.error_expiry_must_come_after_creation); - } - hashedPacketsGen.setKeyExpirationTime(false, numDays * 86400); - } else { - hashedPacketsGen.setKeyExpirationTime(false, 0); - // do this explicitly, although since we're rebuilding, - // this happens anyway - } - - if (saveParcel.primaryIDChanged || - !saveParcel.originalIDs.get(0).equals(saveParcel.userIds.get(0))) { - anyIDChanged = true; - ArrayList> sigList = new ArrayList>(); - for (String userId : saveParcel.userIds) { - String origID = saveParcel.originalIDs.get(userIDIndex); - if (origID.equals(userId) && !saveParcel.newIDs[userIDIndex] && - !userId.equals(saveParcel.originalPrimaryID) && userIDIndex != 0) { - Iterator origSigs = masterPublicKey.getSignaturesForID(origID); - // TODO: make sure this iterator only has signatures we are interested in - while (origSigs.hasNext()) { - PGPSignature origSig = origSigs.next(); - sigList.add(new Pair(origID, origSig)); - } - } else { - PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( - masterPublicKey.getAlgorithm(), HashAlgorithmTags.SHA1) - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); - PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder); - - sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey); - if (userIDIndex == 0) { - sGen.setHashedSubpackets(hashedPacketsGen.generate()); - sGen.setUnhashedSubpackets(unhashedPacketsGen.generate()); - } - PGPSignature certification = sGen.generateCertification(userId, masterPublicKey); - sigList.add(new Pair(userId, certification)); - } - if (!saveParcel.newIDs[userIDIndex]) { - masterPublicKey = PGPPublicKey.removeCertification(masterPublicKey, origID); + switch (algorithmChoice) { + case Constants.choice.algorithm.dsa: { + keyGen = KeyPairGenerator.getInstance("DSA", Constants.BOUNCY_CASTLE_PROVIDER_NAME); + keyGen.initialize(keySize, new SecureRandom()); + algorithm = PGPPublicKey.DSA; + break; } - userIDIndex++; - } - for (Pair toAdd : sigList) { - masterPublicKey = - PGPPublicKey.addCertification(masterPublicKey, toAdd.first, toAdd.second); - } - } else { - for (String userId : saveParcel.userIds) { - String origID = saveParcel.originalIDs.get(userIDIndex); - if (!origID.equals(userId) || saveParcel.newIDs[userIDIndex]) { - anyIDChanged = true; - PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( - masterPublicKey.getAlgorithm(), HashAlgorithmTags.SHA1) - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); - PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder); - - sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey); - if (userIDIndex == 0) { - sGen.setHashedSubpackets(hashedPacketsGen.generate()); - sGen.setUnhashedSubpackets(unhashedPacketsGen.generate()); - } - PGPSignature certification = sGen.generateCertification(userId, masterPublicKey); - if (!saveParcel.newIDs[userIDIndex]) { - masterPublicKey = PGPPublicKey.removeCertification(masterPublicKey, origID); - } - masterPublicKey = - PGPPublicKey.addCertification(masterPublicKey, userId, certification); - } - userIDIndex++; - } - } - ArrayList> sigList = new ArrayList>(); - if (saveParcel.moddedKeys[0]) { - userIDIndex = 0; - for (String userId : saveParcel.userIds) { - String origID = saveParcel.originalIDs.get(userIDIndex); - if (!(origID.equals(saveParcel.originalPrimaryID) && !saveParcel.primaryIDChanged)) { - Iterator sigs = masterPublicKey.getSignaturesForID(userId); - // TODO: make sure this iterator only has signatures we are interested in - while (sigs.hasNext()) { - PGPSignature sig = sigs.next(); - sigList.add(new Pair(userId, sig)); + case Constants.choice.algorithm.elgamal: { + if (isMasterKey) { + throw new PgpGeneralMsgIdException(R.string.error_master_key_must_not_be_el_gamal); } - } - masterPublicKey = PGPPublicKey.removeCertification(masterPublicKey, userId); - userIDIndex++; - } - anyIDChanged = true; - } + keyGen = KeyPairGenerator.getInstance("ElGamal", Constants.BOUNCY_CASTLE_PROVIDER_NAME); + BigInteger p = Primes.getBestPrime(keySize); + BigInteger g = new BigInteger("2"); - //update the keyring with the new ID information - if (anyIDChanged) { - pKR = PGPPublicKeyRing.insertPublicKey(pKR, masterPublicKey); - mKR = PGPSecretKeyRing.replacePublicKeys(mKR, pKR); - } + ElGamalParameterSpec elParams = new ElGamalParameterSpec(p, g); - PGPKeyPair masterKeyPair = new PGPKeyPair(masterPublicKey, masterPrivateKey); - - updateProgress(R.string.progress_building_master_key, 30, 100); - - // define hashing and signing algos - PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build().get( - HashAlgorithmTags.SHA1); - PGPContentSignerBuilder certificationSignerBuilder = new JcaPGPContentSignerBuilder( - masterKeyPair.getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA1); - - // Build key encryptor based on old passphrase, as some keys may be unchanged - PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder( - PGPEncryptedData.CAST5, sha1Calc) - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( - saveParcel.oldPassphrase.toCharArray()); - - //this generates one more signature than necessary... - PGPKeyRingGenerator keyGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, - masterKeyPair, mainUserId, sha1Calc, hashedPacketsGen.generate(), - unhashedPacketsGen.generate(), certificationSignerBuilder, keyEncryptor); - - for (int i = 1; i < saveParcel.keys.size(); ++i) { - updateProgress(40 + 50 * i / saveParcel.keys.size(), 100); - if (saveParcel.moddedKeys[i]) { - PGPSecretKey subKey = saveParcel.keys.get(i).getSecretKeyExternal(); - PGPPublicKey subPublicKey = subKey.getPublicKey(); - - PBESecretKeyDecryptor keyDecryptor2; - if (saveParcel.newKeys[i]) { - keyDecryptor2 = new JcePBESecretKeyDecryptorBuilder() - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( - "".toCharArray()); - } else { - keyDecryptor2 = new JcePBESecretKeyDecryptorBuilder() - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( - saveParcel.oldPassphrase.toCharArray()); - } - PGPPrivateKey subPrivateKey = subKey.extractPrivateKey(keyDecryptor2); - PGPKeyPair subKeyPair = new PGPKeyPair(subPublicKey, subPrivateKey); - - hashedPacketsGen = new PGPSignatureSubpacketGenerator(); - unhashedPacketsGen = new PGPSignatureSubpacketGenerator(); - - usageId = saveParcel.keysUsages.get(i); - canSign = (usageId & KeyFlags.SIGN_DATA) > 0; //todo - separate function for this - if (canSign) { - Date todayDate = new Date(); //both sig times the same - // cross-certify signing keys - hashedPacketsGen.setSignatureCreationTime(false, todayDate); //set outer creation time - PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator(); - subHashedPacketsGen.setSignatureCreationTime(false, todayDate); //set inner creation time - PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( - subPublicKey.getAlgorithm(), PGPUtil.SHA1) - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); - PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder); - sGen.init(PGPSignature.PRIMARYKEY_BINDING, subPrivateKey); - sGen.setHashedSubpackets(subHashedPacketsGen.generate()); - PGPSignature certification = sGen.generateCertification(masterPublicKey, - subPublicKey); - unhashedPacketsGen.setEmbeddedSignature(false, certification); - } - hashedPacketsGen.setKeyFlags(false, usageId); - - if (saveParcel.keysExpiryDates.get(i) != null) { - Calendar creationDate = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - creationDate.setTime(subPublicKey.getCreationTime()); - Calendar expiryDate = saveParcel.keysExpiryDates.get(i); - // note that the below, (a/c) - (b/c) is *not* the same as (a - b) /c - // here we purposefully ignore partial days in each date - long type has - // no fractional part! - long numDays = (expiryDate.getTimeInMillis() / 86400000) - - (creationDate.getTimeInMillis() / 86400000); - if (numDays <= 0) { - throw new PgpGeneralMsgIdException(R.string.error_expiry_must_come_after_creation); - } - hashedPacketsGen.setKeyExpirationTime(false, numDays * 86400); - } else { - hashedPacketsGen.setKeyExpirationTime(false, 0); - // do this explicitly, although since we're rebuilding, - // this happens anyway + keyGen.initialize(elParams); + algorithm = PGPPublicKey.ELGAMAL_ENCRYPT; + break; } - keyGen.addSubKey(subKeyPair, hashedPacketsGen.generate(), unhashedPacketsGen.generate()); - // certifications will be discarded if the key is changed, because I think, for a start, - // they will be invalid. Binding certs are regenerated anyway, and other certs which - // need to be kept are on IDs and attributes - // TODO: don't let revoked keys be edited, other than removed - changing one would - // result in the revocation being wrong? - } - } + case Constants.choice.algorithm.rsa: { + keyGen = KeyPairGenerator.getInstance("RSA", Constants.BOUNCY_CASTLE_PROVIDER_NAME); + keyGen.initialize(keySize, new SecureRandom()); - PGPSecretKeyRing updatedSecretKeyRing = keyGen.generateSecretKeyRing(); - //finally, update the keyrings - Iterator itr = updatedSecretKeyRing.getSecretKeys(); - while (itr.hasNext()) { - PGPSecretKey theNextKey = itr.next(); - if ((theNextKey.isMasterKey() && saveParcel.moddedKeys[0]) || !theNextKey.isMasterKey()) { - mKR = PGPSecretKeyRing.insertSecretKey(mKR, theNextKey); - pKR = PGPPublicKeyRing.insertPublicKey(pKR, theNextKey.getPublicKey()); - } - } + algorithm = PGPPublicKey.RSA_GENERAL; + break; + } - //replace lost IDs - if (saveParcel.moddedKeys[0]) { - masterPublicKey = mKR.getPublicKey(); - for (Pair toAdd : sigList) { - masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, toAdd.first, toAdd.second); + default: { + throw new PgpGeneralMsgIdException(R.string.error_unknown_algorithm_choice); + } } - pKR = PGPPublicKeyRing.insertPublicKey(pKR, masterPublicKey); - mKR = PGPSecretKeyRing.replacePublicKeys(mKR, pKR); - } - - // Build key encryptor based on new passphrase - PBESecretKeyEncryptor keyEncryptorNew = new JcePBESecretKeyEncryptorBuilder( - PGPEncryptedData.CAST5, sha1Calc) - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( - saveParcel.newPassphrase.toCharArray()); - - //update the passphrase - mKR = PGPSecretKeyRing.copyWithNewPassword(mKR, keyDecryptor, keyEncryptorNew); - /* additional handy debug info + // build new key pair + PGPKeyPair keyPair = new JcaPGPKeyPair(algorithm, keyGen.generateKeyPair(), new Date()); - Log.d(Constants.TAG, " ------- in private key -------"); + // define hashing and signing algos + PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build().get( + HashAlgorithmTags.SHA1); - for(String uid : new IterableIterator(secretKeyRing.getPublicKey().getUserIDs())) { - for(PGPSignature sig : new IterableIterator( - secretKeyRing.getPublicKey().getSignaturesForId(uid))) { - Log.d(Constants.TAG, "sig: " + - PgpKeyHelper.convertKeyIdToHex(sig.getKeyID()) + " for " + uid); - } - - } - - Log.d(Constants.TAG, " ------- in public key -------"); + // Build key encrypter and decrypter based on passphrase + PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder( + PGPEncryptedData.CAST5, sha1Calc) + .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); - for(String uid : new IterableIterator(publicKeyRing.getPublicKey().getUserIDs())) { - for(PGPSignature sig : new IterableIterator( - publicKeyRing.getPublicKey().getSignaturesForId(uid))) { - Log.d(Constants.TAG, "sig: " + - PgpKeyHelper.convertKeyIdToHex(sig.getKeyID()) + " for " + uid); - } + return new PGPSecretKey(keyPair.getPrivateKey(), keyPair.getPublicKey(), + sha1Calc, isMasterKey, keyEncryptor); + } catch(NoSuchProviderException e) { + throw new PgpGeneralMsgIdException(R.string.error_encoding, e); + } catch(NoSuchAlgorithmException e) { + throw new PgpGeneralMsgIdException(R.string.error_encoding, e); + } catch(InvalidAlgorithmParameterException e) { + throw new PgpGeneralMsgIdException(R.string.error_encoding, e); + } catch(PGPException e) { + throw new PgpGeneralMsgIdException(R.string.error_encoding, e); } - - */ - - return new Pair(new UncachedKeyRing(pKR), - new UncachedKeyRing(mKR)); - } public Pair buildSecretKey(PGPSecretKeyRing sKR, diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java index 3ddcdfcf4..d3f46a7a4 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -44,7 +44,6 @@ import org.sufficientlysecure.keychain.pgp.PgpKeyOperation; import org.sufficientlysecure.keychain.pgp.PgpSignEncrypt; import org.sufficientlysecure.keychain.pgp.Progressable; import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; -import org.sufficientlysecure.keychain.pgp.UncachedSecretKey; import org.sufficientlysecure.keychain.pgp.WrappedPublicKeyRing; import org.sufficientlysecure.keychain.pgp.WrappedSecretKey; import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing; @@ -87,9 +86,6 @@ public class KeychainIntentService extends IntentService public static final String ACTION_DECRYPT_VERIFY = Constants.INTENT_PREFIX + "DECRYPT_VERIFY"; public static final String ACTION_SAVE_KEYRING = Constants.INTENT_PREFIX + "SAVE_KEYRING"; - public static final String ACTION_GENERATE_KEY = Constants.INTENT_PREFIX + "GENERATE_KEY"; - public static final String ACTION_GENERATE_DEFAULT_RSA_KEYS = Constants.INTENT_PREFIX - + "GENERATE_DEFAULT_RSA_KEYS"; public static final String ACTION_DELETE_FILE_SECURELY = Constants.INTENT_PREFIX + "DELETE_FILE_SECURELY"; @@ -127,14 +123,7 @@ public class KeychainIntentService extends IntentService // save keyring public static final String SAVE_KEYRING_PARCEL = "save_parcel"; - public static final String SAVE_KEYRING_CAN_SIGN = "can_sign"; - - - // generate key - public static final String GENERATE_KEY_ALGORITHM = "algorithm"; - public static final String GENERATE_KEY_KEY_SIZE = "key_size"; - public static final String GENERATE_KEY_SYMMETRIC_PASSPHRASE = "passphrase"; - public static final String GENERATE_KEY_MASTER_KEY = "master_key"; + public static final String SAVE_KEYRING_PASSPHRASE = "passphrase"; // delete file securely public static final String DELETE_FILE = "deleteFile"; @@ -164,9 +153,6 @@ public class KeychainIntentService extends IntentService /* * possible data keys as result send over messenger */ - // keys - public static final String RESULT_NEW_KEY = "new_key"; - public static final String RESULT_KEY_USAGES = "new_key_usages"; // encrypt public static final String RESULT_BYTES = "encrypted_data"; @@ -490,133 +476,35 @@ public class KeychainIntentService extends IntentService } else if (ACTION_SAVE_KEYRING.equals(action)) { try { /* Input */ - OldSaveKeyringParcel saveParcel = data.getParcelable(SAVE_KEYRING_PARCEL); - String oldPassphrase = saveParcel.oldPassphrase; - String newPassphrase = saveParcel.newPassphrase; - boolean canSign = true; - - if (data.containsKey(SAVE_KEYRING_CAN_SIGN)) { - canSign = data.getBoolean(SAVE_KEYRING_CAN_SIGN); - } - - if (newPassphrase == null) { - newPassphrase = oldPassphrase; - } - - long masterKeyId = saveParcel.keys.get(0).getKeyId(); + SaveKeyringParcel saveParcel = data.getParcelable(SAVE_KEYRING_PARCEL); + long masterKeyId = saveParcel.mMasterKeyId; /* Operation */ ProviderHelper providerHelper = new ProviderHelper(this); - if (!canSign) { - setProgress(R.string.progress_building_key, 0, 100); - WrappedSecretKeyRing keyRing = providerHelper.getWrappedSecretKeyRing(masterKeyId); - UncachedKeyRing newKeyRing = - keyRing.changeSecretKeyPassphrase(oldPassphrase, newPassphrase); - setProgress(R.string.progress_saving_key_ring, 50, 100); - providerHelper.saveSecretKeyRing(newKeyRing); - setProgress(R.string.progress_done, 100, 100); - } else { - PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 90, 100)); - try { - WrappedSecretKeyRing seckey = providerHelper.getWrappedSecretKeyRing(masterKeyId); - WrappedPublicKeyRing pubkey = providerHelper.getWrappedPublicKeyRing(masterKeyId); - - PgpKeyOperation.Pair pair = - keyOperations.buildSecretKey(seckey, pubkey, saveParcel); // edit existing - setProgress(R.string.progress_saving_key_ring, 90, 100); - providerHelper.saveKeyRing(pair.first, pair.second); - } catch (ProviderHelper.NotFoundException e) { - PgpKeyOperation.Pair pair = - keyOperations.buildNewSecretKey(saveParcel); //new Keyring - // save the pair - setProgress(R.string.progress_saving_key_ring, 90, 100); - providerHelper.saveKeyRing(pair.first, pair.second); - } - - setProgress(R.string.progress_done, 100, 100); + PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 90, 100)); + try { + String passphrase = data.getString(SAVE_KEYRING_PASSPHRASE); + WrappedSecretKeyRing secRing = providerHelper.getWrappedSecretKeyRing(masterKeyId); + + UncachedKeyRing ring = keyOperations.modifySecretKeyRing(secRing, saveParcel, passphrase); + setProgress(R.string.progress_saving_key_ring, 90, 100); + providerHelper.saveSecretKeyRing(ring); + } catch (ProviderHelper.NotFoundException e) { + // UncachedKeyRing ring = keyOperations.(saveParcel); //new Keyring + // save the pair + setProgress(R.string.progress_saving_key_ring, 90, 100); + // providerHelper.saveSecretKeyRing(ring); + sendErrorToHandler(e); } - PassphraseCacheService.addCachedPassphrase(this, masterKeyId, newPassphrase); - /* Output */ - sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY); - } catch (Exception e) { - sendErrorToHandler(e); - } - } else if (ACTION_GENERATE_KEY.equals(action)) { - try { - /* Input */ - int algorithm = data.getInt(GENERATE_KEY_ALGORITHM); - String passphrase = data.getString(GENERATE_KEY_SYMMETRIC_PASSPHRASE); - int keysize = data.getInt(GENERATE_KEY_KEY_SIZE); - boolean masterKey = data.getBoolean(GENERATE_KEY_MASTER_KEY); + setProgress(R.string.progress_done, 100, 100); - /* Operation */ - PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 100, 100)); - byte[] newKey = keyOperations.createKey(algorithm, keysize, passphrase, masterKey); - - /* Output */ - Bundle resultData = new Bundle(); - resultData.putByteArray(RESULT_NEW_KEY, newKey); - - OtherHelper.logDebugBundle(resultData, "resultData"); - - sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData); - } catch (Exception e) { - sendErrorToHandler(e); - } - } else if (ACTION_GENERATE_DEFAULT_RSA_KEYS.equals(action)) { - // generate one RSA 4096 key for signing and one subkey for encrypting! - try { - /* Input */ - String passphrase = data.getString(GENERATE_KEY_SYMMETRIC_PASSPHRASE); - ArrayList keyUsageList = new ArrayList(); - - /* Operation */ - int keysTotal = 3; - int keysCreated = 0; - setProgress( - getApplicationContext().getResources(). - getQuantityString(R.plurals.progress_generating, keysTotal), - keysCreated, - keysTotal); - PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 100, 100)); - - ByteArrayOutputStream os = new ByteArrayOutputStream(); - - byte[] buf; - - buf = keyOperations.createKey(Constants.choice.algorithm.rsa, - 4096, passphrase, true); - os.write(buf); - keyUsageList.add(UncachedSecretKey.CERTIFY_OTHER); - keysCreated++; - setProgress(keysCreated, keysTotal); - - buf = keyOperations.createKey(Constants.choice.algorithm.rsa, - 4096, passphrase, false); - os.write(buf); - keyUsageList.add(UncachedSecretKey.ENCRYPT_COMMS | UncachedSecretKey.ENCRYPT_STORAGE); - keysCreated++; - setProgress(keysCreated, keysTotal); - - buf = keyOperations.createKey(Constants.choice.algorithm.rsa, - 4096, passphrase, false); - os.write(buf); - keyUsageList.add(UncachedSecretKey.SIGN_DATA); - keysCreated++; - setProgress(keysCreated, keysTotal); - - // TODO: default to one master for cert, one sub for encrypt and one sub - // for sign + if (saveParcel.newPassphrase != null) { + PassphraseCacheService.addCachedPassphrase(this, masterKeyId, saveParcel.newPassphrase); + } /* Output */ - Bundle resultData = new Bundle(); - resultData.putByteArray(RESULT_NEW_KEY, os.toByteArray()); - resultData.putIntegerArrayList(RESULT_KEY_USAGES, keyUsageList); - - OtherHelper.logDebugBundle(resultData, "resultData"); - - sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData); + sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY); } catch (Exception e) { sendErrorToHandler(e); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OldSaveKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OldSaveKeyringParcel.java deleted file mode 100644 index b722393ad..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OldSaveKeyringParcel.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (C) 2014 Ash Hughes - * - * 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 . - */ - -package org.sufficientlysecure.keychain.service; - -import android.os.Parcel; -import android.os.Parcelable; - -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.pgp.PgpConversionHelper; -import org.sufficientlysecure.keychain.pgp.UncachedSecretKey; -import org.sufficientlysecure.keychain.util.IterableIterator; -import org.sufficientlysecure.keychain.util.Log; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Calendar; - -/** Class for parcelling data between ui and services. - * This class is outdated and scheduled for removal, pending a rewrite of the - * EditKeyActivity and save keyring routines. - */ -@Deprecated -public class OldSaveKeyringParcel implements Parcelable { - - public ArrayList userIds; - public ArrayList originalIDs; - public ArrayList deletedIDs; - public boolean[] newIDs; - public boolean primaryIDChanged; - public boolean[] moddedKeys; - public ArrayList deletedKeys; - public ArrayList keysExpiryDates; - public ArrayList keysUsages; - public String newPassphrase; - public String oldPassphrase; - public boolean[] newKeys; - public ArrayList keys; - public String originalPrimaryID; - - public OldSaveKeyringParcel() {} - - private OldSaveKeyringParcel(Parcel source) { - userIds = (ArrayList) source.readSerializable(); - originalIDs = (ArrayList) source.readSerializable(); - deletedIDs = (ArrayList) source.readSerializable(); - newIDs = source.createBooleanArray(); - primaryIDChanged = source.readByte() != 0; - moddedKeys = source.createBooleanArray(); - byte[] tmp = source.createByteArray(); - if (tmp == null) { - deletedKeys = null; - } else { - deletedKeys = PgpConversionHelper.BytesToPGPSecretKeyList(tmp); - } - keysExpiryDates = (ArrayList) source.readSerializable(); - keysUsages = source.readArrayList(Integer.class.getClassLoader()); - newPassphrase = source.readString(); - oldPassphrase = source.readString(); - newKeys = source.createBooleanArray(); - keys = PgpConversionHelper.BytesToPGPSecretKeyList(source.createByteArray()); - originalPrimaryID = source.readString(); - } - - @Override - public void writeToParcel(Parcel destination, int flags) { - destination.writeSerializable(userIds); //might not be the best method to store. - destination.writeSerializable(originalIDs); - destination.writeSerializable(deletedIDs); - destination.writeBooleanArray(newIDs); - destination.writeByte((byte) (primaryIDChanged ? 1 : 0)); - destination.writeBooleanArray(moddedKeys); - destination.writeByteArray(encodeArrayList(deletedKeys)); - destination.writeSerializable(keysExpiryDates); - destination.writeList(keysUsages); - destination.writeString(newPassphrase); - destination.writeString(oldPassphrase); - destination.writeBooleanArray(newKeys); - destination.writeByteArray(encodeArrayList(keys)); - destination.writeString(originalPrimaryID); - } - - public static final Creator CREATOR = new Creator() { - public OldSaveKeyringParcel createFromParcel(final Parcel source) { - return new OldSaveKeyringParcel(source); - } - - public OldSaveKeyringParcel[] newArray(final int size) { - return new OldSaveKeyringParcel[size]; - } - }; - - private static byte[] encodeArrayList(ArrayList list) { - if(list.isEmpty()) { - return null; - } - - ByteArrayOutputStream os = new ByteArrayOutputStream(); - for(UncachedSecretKey key : new IterableIterator(list.iterator())) { - try { - key.encodeSecretKey(os); - } catch (IOException e) { - Log.e(Constants.TAG, "Error while converting ArrayList to byte[]!", e); - } - } - return os.toByteArray(); - } - - @Override - public int describeContents() { - return 0; - } -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java index 3514ab2e5..ca7da5ae3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java @@ -23,7 +23,7 @@ import java.util.HashMap; public class SaveKeyringParcel implements Parcelable { // the master key id to be edited - private final long mMasterKeyId; + public final long mMasterKeyId; // the key fingerprint, for safety private final byte[] mFingerprint; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java index bcb2286ab..4309e3505 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java @@ -57,7 +57,6 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; -import org.sufficientlysecure.keychain.service.OldSaveKeyringParcel; import org.sufficientlysecure.keychain.service.PassphraseCacheService; import org.sufficientlysecure.keychain.ui.dialog.CustomAlertDialogBuilder; import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment; @@ -199,13 +198,10 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener // generate key if (extras.containsKey(EXTRA_GENERATE_DEFAULT_KEYS)) { + /* boolean generateDefaultKeys = extras.getBoolean(EXTRA_GENERATE_DEFAULT_KEYS); if (generateDefaultKeys) { - // Send all information needed to service generate keys in other thread - final Intent serviceIntent = new Intent(this, KeychainIntentService.class); - serviceIntent.setAction(KeychainIntentService.ACTION_GENERATE_DEFAULT_RSA_KEYS); - // fill values for this action Bundle data = new Bundle(); data.putString(KeychainIntentService.GENERATE_KEY_SYMMETRIC_PASSPHRASE, @@ -265,6 +261,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener // start service with intent startService(serviceIntent); } + */ } } else { buildLayout(false); @@ -547,6 +544,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener } private void finallySaveClicked() { + /* try { // Send all information needed to service to edit key in other thread Intent intent = new Intent(this, KeychainIntentService.class); @@ -609,6 +607,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener AppMsg.makeText(this, getString(R.string.error_message, e.getMessage()), AppMsg.STYLE_ALERT).show(); } + */ } private void cancelClicked() { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java index 3e8e18ce5..b7336318f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java @@ -346,13 +346,8 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor } private void createKey() { - // Send all information needed to service to edit key in other thread - final Intent intent = new Intent(mActivity, KeychainIntentService.class); - - intent.setAction(KeychainIntentService.ACTION_GENERATE_KEY); // fill values for this action - Bundle data = new Bundle(); Boolean isMasterKey; String passphrase; @@ -365,6 +360,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor passphrase = ""; isMasterKey = true; } + /* data.putBoolean(KeychainIntentService.GENERATE_KEY_MASTER_KEY, isMasterKey); data.putString(KeychainIntentService.GENERATE_KEY_SYMMETRIC_PASSPHRASE, passphrase); data.putInt(KeychainIntentService.GENERATE_KEY_ALGORITHM, mNewKeyAlgorithmChoice.getId()); @@ -410,6 +406,8 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor // start service with intent mActivity.startService(intent); + */ + } private void addGeneratedKeyToView(UncachedSecretKey newKey) { -- cgit v1.2.3 From 5c47143d64e8ab2e1ef00f5134d9362c08e6f529 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 17 Jun 2014 20:04:25 +0200 Subject: new-edit: add new save keyring stuff --- .../keychain/pgp/PgpKeyOperation.java | 264 +++++++++------------ .../pgp/exception/PgpGeneralMsgIdException.java | 5 + .../keychain/service/SaveKeyringParcel.java | 6 +- 3 files changed, 124 insertions(+), 151 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') 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 e9d8fd295..e08c96dad 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java @@ -171,173 +171,139 @@ public class PgpKeyOperation { return new PGPSecretKey(keyPair.getPrivateKey(), keyPair.getPublicKey(), sha1Calc, isMasterKey, keyEncryptor); } catch(NoSuchProviderException e) { - throw new PgpGeneralMsgIdException(R.string.error_encoding, e); + throw new RuntimeException(e); } catch(NoSuchAlgorithmException e) { - throw new PgpGeneralMsgIdException(R.string.error_encoding, e); + throw new RuntimeException(e); } catch(InvalidAlgorithmParameterException e) { - throw new PgpGeneralMsgIdException(R.string.error_encoding, e); + throw new RuntimeException(e); } catch(PGPException e) { - throw new PgpGeneralMsgIdException(R.string.error_encoding, e); + throw new PgpGeneralMsgIdException(R.string.msg_mr_error_pgp, e); } } - public Pair buildSecretKey(PGPSecretKeyRing sKR, - PGPPublicKeyRing pKR, - SaveKeyringParcel saveParcel, - String passphrase) + /** This method introduces a list of modifications specified by a SaveKeyringParcel to a + * PGPSecretKeyRing. + * + * Note that PGPPublicKeyRings can not be directly modified. Instead, the corresponding + * PGPSecretKeyRing must be modified and consequently consolidated with its public counterpart. + * This is a natural workflow since pgp keyrings are immutable data structures: Old semantics + * are changed by adding new certificates, which implicitly override older certificates. + * + */ + public UncachedKeyRing modifySecretKeyRing(WrappedSecretKeyRing wsKR, SaveKeyringParcel saveParcel, + String passphrase) throws PgpGeneralMsgIdException, PGPException, SignatureException, IOException { - updateProgress(R.string.progress_building_key, 0, 100); - - // sort these, so we can use binarySearch later on - Arrays.sort(saveParcel.revokeSubKeys); - Arrays.sort(saveParcel.revokeUserIds); - /* - * What's gonna happen here: - * * 1. Unlock private key - * - * 2. Create new secret key ring - * - * 3. Copy subkeys - * - Generate revocation if requested - * - Copy old cert, or generate new if change requested - * - * 4. Generate and add new subkeys - * - * 5. Copy user ids - * - Generate revocation if requested - * - Copy old cert, or generate new if primary user id status changed - * - * 6. Add new user ids - * - * 7. Generate PublicKeyRing from SecretKeyRing - * - * 8. Return pair (PublicKeyRing,SecretKeyRing) - * + * 2a. Add certificates for new user ids + * 2b. Add revocations for revoked user ids + * 3. If primary user id changed, generate new certificates for both old and new + * 4a. For each subkey change, generate new subkey binding certificate + * 4b. For each subkey revocation, generate new subkey revocation certificate + * 5. Generate and add new subkeys + * 6. If requested, change passphrase */ - // 1. Unlock private key updateProgress(R.string.progress_building_key, 0, 100); + // We work on bouncycastle object level here + PGPSecretKeyRing sKR = wsKR.getRing(); PGPPublicKey masterPublicKey = sKR.getPublicKey(); + PGPSecretKey masterSecretKey = sKR.getSecretKey(); + + // 1. Unlock private key PGPPrivateKey masterPrivateKey; { - PGPSecretKey masterKey = sKR.getSecretKey(); PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); - masterPrivateKey = masterKey.extractPrivateKey(keyDecryptor); + masterPrivateKey = masterSecretKey.extractPrivateKey(keyDecryptor); + } + if (!Arrays.equals(saveParcel.mFingerprint, sKR.getPublicKey().getFingerprint())) { + return null; } - // 2. Create new secret key ring updateProgress(R.string.progress_certifying_master_key, 20, 100); - // Note we do NOT use PGPKeyRingGeneraor, it's just one level too high and does stuff - // we want to do manually. Instead, we simply use a list of secret keys. - ArrayList secretKeys = new ArrayList(); - ArrayList publicKeys = new ArrayList(); + { // work on master secret key - // 3. Copy subkeys - // - Generate revocation if requested - // - Copy old cert, or generate new if change requested - for (PGPSecretKey sKey : new IterableIterator(sKR.getSecretKeys())) { - PGPPublicKey pKey = sKey.getPublicKey(); - if (Arrays.binarySearch(saveParcel.revokeSubKeys, sKey.getKeyID()) >= 0) { - // add revocation signature to key, if there is none yet - if (!pKey.getSignaturesOfType(PGPSignature.SUBKEY_REVOCATION).hasNext()) { - // generate revocation signature - } - } - if (saveParcel.changeSubKeys.containsKey(sKey.getKeyID())) { - // change subkey flags? - SaveKeyringParcel.SubkeyChange change = saveParcel.changeSubKeys.get(sKey.getKeyID()); - // remove old subkey binding signature(s?) - for (PGPSignature sig : new IterableIterator( - pKey.getSignaturesOfType(PGPSignature.SUBKEY_BINDING))) { - pKey = PGPPublicKey.removeCertification(pKey, sig); - } + PGPPublicKey modifiedPublicKey = masterPublicKey; - // generate and add new signature - PGPSignature sig = generateSubkeyBindingSignature(masterPublicKey, masterPrivateKey, - sKey, pKey, change.mFlags, change.mExpiry, passphrase); - pKey = PGPPublicKey.addCertification(pKey, sig); + // 2a. Add certificates for new user ids + for (String userId : saveParcel.addUserIds) { + PGPSignature cert = generateUserIdSignature(masterPrivateKey, + masterPublicKey, userId, false); + modifiedPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, cert); } - secretKeys.add(PGPSecretKey.replacePublicKey(sKey, pKey)); - publicKeys.add(pKey); - } - // 4. Generate and add new subkeys - // TODO - - // 5. Copy user ids - for (String userId : new IterableIterator(masterPublicKey.getUserIDs())) { - // - Copy old cert, or generate new if primary user id status changed - boolean certified = false, revoked = false; - for (PGPSignature sig : new IterableIterator( - masterPublicKey.getSignaturesForID(userId))) { - // We know there are only revocation and certification types in here. - switch(sig.getSignatureType()) { - case PGPSignature.CERTIFICATION_REVOCATION: - revoked = true; - continue; - - case PGPSignature.DEFAULT_CERTIFICATION: - case PGPSignature.NO_CERTIFICATION: - case PGPSignature.CASUAL_CERTIFICATION: - case PGPSignature.POSITIVE_CERTIFICATION: - // Already got one? Remove this one, then. - if (certified) { - masterPublicKey = PGPPublicKey.removeCertification( - masterPublicKey, userId, sig); - continue; - } - boolean primary = userId.equals(saveParcel.changePrimaryUserId); - // Generate a new one under certain circumstances - if (saveParcel.changePrimaryUserId != null && - sig.getHashedSubPackets().isPrimaryUserID() != primary) { - PGPSignature cert = generateUserIdSignature( - masterPrivateKey, masterPublicKey, userId, primary); - PGPPublicKey.addCertification(masterPublicKey, userId, cert); - } - certified = true; - } - } - // - Generate revocation if requested - if (!revoked && Arrays.binarySearch(saveParcel.revokeUserIds, userId) >= 0) { + // 2b. Add revocations for revoked user ids + for (String userId : saveParcel.revokeUserIds) { PGPSignature cert = generateRevocationSignature(masterPrivateKey, masterPublicKey, userId); - masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, cert); + modifiedPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, cert); + } + + // 3. If primary user id changed, generate new certificates for both old and new + if (saveParcel.changePrimaryUserId != null) { + // todo + } + + // Update the secret key ring + if (modifiedPublicKey != masterPublicKey) { + masterSecretKey = PGPSecretKey.replacePublicKey(masterSecretKey, modifiedPublicKey); + masterPublicKey = modifiedPublicKey; + sKR = PGPSecretKeyRing.insertSecretKey(sKR, masterSecretKey); } - } - // 6. Add new user ids - for(String userId : saveParcel.addUserIds) { - PGPSignature cert = generateUserIdSignature(masterPrivateKey, - masterPublicKey, userId, userId.equals(saveParcel.changePrimaryUserId)); - masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, cert); } - // 7. Generate PublicKeyRing from SecretKeyRing - updateProgress(R.string.progress_building_master_key, 30, 100); - PGPSecretKeyRing ring = new PGPSecretKeyRing(secretKeys); - // Copy all non-self uid certificates - for (String userId : new IterableIterator(masterPublicKey.getUserIDs())) { - // - Copy old cert, or generate new if primary user id status changed - boolean certified = false, revoked = false; - for (PGPSignature sig : new IterableIterator( - masterPublicKey.getSignaturesForID(userId))) { + // 4a. For each subkey change, generate new subkey binding certificate + for (SaveKeyringParcel.SubkeyChange change : saveParcel.changeSubKeys) { + PGPSecretKey sKey = sKR.getSecretKey(change.mKeyId); + if (sKey == null) { + return null; } + PGPPublicKey pKey = sKey.getPublicKey(); + + // generate and add new signature + PGPSignature sig = generateSubkeyBindingSignature(masterPublicKey, masterPrivateKey, + sKey, pKey, change.mFlags, change.mExpiry, passphrase); + pKey = PGPPublicKey.addCertification(pKey, sig); + sKR = PGPSecretKeyRing.insertSecretKey(sKR, PGPSecretKey.replacePublicKey(sKey, pKey)); } - for (PGPPublicKey newKey : publicKeys) { - PGPPublicKey oldKey = pKR.getPublicKey(newKey.getKeyID()); - for (PGPSignature sig : new IterableIterator( - oldKey.getSignatures())) { + // 4b. For each subkey change, generate new subkey binding certificate + for (long revocation : saveParcel.revokeSubKeys) { + PGPSecretKey sKey = sKR.getSecretKey(revocation); + if (sKey == null) { + return null; + } + PGPPublicKey pKey = sKey.getPublicKey(); + + // generate and add new signature + PGPSignature sig = generateRevocationSignature(masterPublicKey, masterPrivateKey, pKey); + + pKey = PGPPublicKey.addCertification(pKey, sig); + sKR = PGPSecretKeyRing.insertSecretKey(sKR, PGPSecretKey.replacePublicKey(sKey, pKey)); + } + + // 5. Generate and add new subkeys + for (SaveKeyringParcel.SubkeyAdd add : saveParcel.addSubKeys) { + try { + PGPSecretKey sKey = createKey(add.mAlgorithm, add.mKeysize, passphrase, false); + PGPPublicKey pKey = sKey.getPublicKey(); + PGPSignature cert = generateSubkeyBindingSignature(masterPublicKey, masterPrivateKey, + sKey, pKey, add.mFlags, add.mExpiry, passphrase); + + pKey = PGPPublicKey.addCertification(pKey, cert); + sKey = PGPSecretKey.replacePublicKey(sKey, pKey); + sKR = PGPSecretKeyRing.insertSecretKey(sKR, PGPSecretKey.replacePublicKey(sKey, pKey)); + } catch (PgpGeneralMsgIdException e) { + return null; } } - // If requested, set new passphrase + // 6. If requested, change passphrase if (saveParcel.newPassphrase != null) { PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build() .get(HashAlgorithmTags.SHA1); @@ -352,9 +318,7 @@ public class PgpKeyOperation { sKR = PGPSecretKeyRing.copyWithNewPassword(sKR, keyDecryptor, keyEncryptorNew); } - // 8. Return pair (PublicKeyRing,SecretKeyRing) - - return new Pair(sKR, pKR); + return new UncachedKeyRing(sKR); } @@ -390,10 +354,29 @@ public class PgpKeyOperation { return sGen.generateCertification(userId, pKey); } + private static PGPSignature generateRevocationSignature( + PGPPublicKey masterPublicKey, PGPPrivateKey masterPrivateKey, PGPPublicKey pKey) + throws IOException, PGPException, SignatureException { + PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( + pKey.getAlgorithm(), PGPUtil.SHA1) + .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); + PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder); + PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator(); + subHashedPacketsGen.setSignatureCreationTime(false, new Date()); + sGen.setHashedSubpackets(subHashedPacketsGen.generate()); + // Generate key revocation or subkey revocation, depending on master/subkey-ness + if (masterPublicKey.getKeyID() == pKey.getKeyID()) { + sGen.init(PGPSignature.KEY_REVOCATION, masterPrivateKey); + return sGen.generateCertification(masterPublicKey); + } else { + sGen.init(PGPSignature.SUBKEY_REVOCATION, masterPrivateKey); + return sGen.generateCertification(masterPublicKey, pKey); + } + } + private static PGPSignature generateSubkeyBindingSignature( PGPPublicKey masterPublicKey, PGPPrivateKey masterPrivateKey, - PGPSecretKey sKey, PGPPublicKey pKey, - int flags, Long expiry, String passphrase) + PGPSecretKey sKey, PGPPublicKey pKey, int flags, Long expiry, String passphrase) throws PgpGeneralMsgIdException, IOException, PGPException, SignatureException { // date for signing @@ -510,19 +493,4 @@ public class PgpKeyOperation { return publicKey; } - /** - * Simple static subclass that stores two values. - *

- * This is only used to return a pair of values in one function above. We specifically don't use - * com.android.Pair to keep this class free from android dependencies. - */ - public static class Pair { - public final K first; - public final V second; - - public Pair(K first, V second) { - this.first = first; - this.second = second; - } - } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralMsgIdException.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralMsgIdException.java index 3b88897ed..3700b4c34 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralMsgIdException.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralMsgIdException.java @@ -30,6 +30,11 @@ public class PgpGeneralMsgIdException extends Exception { mMessageId = messageId; } + public PgpGeneralMsgIdException(int messageId, Throwable cause) { + super("msg[" + messageId + "]", cause); + mMessageId = messageId; + } + public PgpGeneralException getContextualized(Context context) { return new PgpGeneralException(context.getString(mMessageId), this); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java index ca7da5ae3..c68b7c189 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java @@ -25,14 +25,14 @@ public class SaveKeyringParcel implements Parcelable { // the master key id to be edited public final long mMasterKeyId; // the key fingerprint, for safety - private final byte[] mFingerprint; + public final byte[] mFingerprint; public String newPassphrase; public String[] addUserIds; public SubkeyAdd[] addSubKeys; - public HashMap changeSubKeys; + public SubkeyChange[] changeSubKeys; public String changePrimaryUserId; public String[] revokeUserIds; @@ -76,7 +76,7 @@ public class SaveKeyringParcel implements Parcelable { addUserIds = source.createStringArray(); addSubKeys = (SubkeyAdd[]) source.readSerializable(); - changeSubKeys = (HashMap) source.readSerializable(); + changeSubKeys = (SubkeyChange[]) source.readSerializable(); changePrimaryUserId = source.readString(); revokeUserIds = source.createStringArray(); -- cgit v1.2.3 From 4bff50bffc43c23e08f87ff7c5b19fe790874d01 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 17 Jun 2014 20:40:26 +0200 Subject: new-edit: add logging to modifySecretKeyRing operation --- .../keychain/pgp/PgpKeyOperation.java | 184 +++++++++++++-------- .../keychain/service/KeychainIntentService.java | 5 +- .../keychain/service/OperationResultParcel.java | 22 +++ 3 files changed, 139 insertions(+), 72 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') 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 e08c96dad..bbdbfffd2 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java @@ -47,6 +47,9 @@ import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException; +import org.sufficientlysecure.keychain.service.OperationResultParcel.LogLevel; +import org.sufficientlysecure.keychain.service.OperationResultParcel.LogType; +import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog; import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.Primes; @@ -98,12 +101,6 @@ public class PgpKeyOperation { } } - void updateProgress(int current, int total) { - if (mProgress != null) { - mProgress.setProgress(current, total); - } - } - /** Creates new secret key. */ private PGPSecretKey createKey(int algorithmChoice, int keySize, String passphrase, boolean isMasterKey) throws PgpGeneralMsgIdException { @@ -182,7 +179,9 @@ public class PgpKeyOperation { } /** This method introduces a list of modifications specified by a SaveKeyringParcel to a - * PGPSecretKeyRing. + * WrappedSecretKeyRing. + * + * This method relies on WrappedSecretKeyRing's canonicalization property! * * Note that PGPPublicKeyRings can not be directly modified. Instead, the corresponding * PGPSecretKeyRing must be modified and consequently consolidated with its public counterpart. @@ -191,8 +190,7 @@ public class PgpKeyOperation { * */ public UncachedKeyRing modifySecretKeyRing(WrappedSecretKeyRing wsKR, SaveKeyringParcel saveParcel, - String passphrase) - throws PgpGeneralMsgIdException, PGPException, SignatureException, IOException { + String passphrase, OperationLog log, int indent) { /* * 1. Unlock private key @@ -205,6 +203,8 @@ public class PgpKeyOperation { * 6. If requested, change passphrase */ + log.add(LogLevel.START, LogType.MSG_MR, indent); + indent += 1; updateProgress(R.string.progress_building_key, 0, 100); // We work on bouncycastle object level here @@ -213,10 +213,16 @@ public class PgpKeyOperation { PGPSecretKey masterSecretKey = sKR.getSecretKey(); // 1. Unlock private key + log.add(LogLevel.DEBUG, LogType.MSG_MR_UNLOCK, indent); PGPPrivateKey masterPrivateKey; { - PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( - Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); - masterPrivateKey = masterSecretKey.extractPrivateKey(keyDecryptor); + try { + PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( + Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); + masterPrivateKey = masterSecretKey.extractPrivateKey(keyDecryptor); + } catch (PGPException e) { + log.add(LogLevel.ERROR, LogType.MSG_MR_UNLOCK_ERROR, indent+1); + return null; + } } if (!Arrays.equals(saveParcel.mFingerprint, sKR.getPublicKey().getFingerprint())) { return null; @@ -224,12 +230,14 @@ public class PgpKeyOperation { updateProgress(R.string.progress_certifying_master_key, 20, 100); - { // work on master secret key + // work on master secret key + try { PGPPublicKey modifiedPublicKey = masterPublicKey; // 2a. Add certificates for new user ids for (String userId : saveParcel.addUserIds) { + log.add(LogLevel.INFO, LogType.MSG_MR_UID_ADD, indent); PGPSignature cert = generateUserIdSignature(masterPrivateKey, masterPublicKey, userId, false); modifiedPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, cert); @@ -237,6 +245,7 @@ public class PgpKeyOperation { // 2b. Add revocations for revoked user ids for (String userId : saveParcel.revokeUserIds) { + log.add(LogLevel.INFO, LogType.MSG_MR_UID_REVOKE, indent); PGPSignature cert = generateRevocationSignature(masterPrivateKey, masterPublicKey, userId); modifiedPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, cert); @@ -244,6 +253,7 @@ public class PgpKeyOperation { // 3. If primary user id changed, generate new certificates for both old and new if (saveParcel.changePrimaryUserId != null) { + log.add(LogLevel.INFO, LogType.MSG_MR_UID_PRIMARY, indent); // todo } @@ -254,70 +264,105 @@ public class PgpKeyOperation { sKR = PGPSecretKeyRing.insertSecretKey(sKR, masterSecretKey); } - } - // 4a. For each subkey change, generate new subkey binding certificate - for (SaveKeyringParcel.SubkeyChange change : saveParcel.changeSubKeys) { - PGPSecretKey sKey = sKR.getSecretKey(change.mKeyId); - if (sKey == null) { - return null; - } - PGPPublicKey pKey = sKey.getPublicKey(); + for (SaveKeyringParcel.SubkeyChange change : saveParcel.changeSubKeys) { + log.add(LogLevel.INFO, LogType.MSG_MR_SUBKEY_CHANGE, + new String[]{PgpKeyHelper.convertKeyIdToHex(change.mKeyId)}, indent); + PGPSecretKey sKey = sKR.getSecretKey(change.mKeyId); + if (sKey == null) { + log.add(LogLevel.ERROR, LogType.MSG_MR_SUBKEY_MISSING, + new String[]{PgpKeyHelper.convertKeyIdToHex(change.mKeyId)}, indent + 1); + return null; + } + PGPPublicKey pKey = sKey.getPublicKey(); - // generate and add new signature - PGPSignature sig = generateSubkeyBindingSignature(masterPublicKey, masterPrivateKey, - sKey, pKey, change.mFlags, change.mExpiry, passphrase); - pKey = PGPPublicKey.addCertification(pKey, sig); - sKR = PGPSecretKeyRing.insertSecretKey(sKR, PGPSecretKey.replacePublicKey(sKey, pKey)); - } + if (change.mExpiry != null && new Date(change.mExpiry).before(new Date())) { + log.add(LogLevel.ERROR, LogType.MSG_MR_SUBKEY_PAST_EXPIRY, + new String[]{PgpKeyHelper.convertKeyIdToHex(change.mKeyId)}, indent + 1); + return null; + } - // 4b. For each subkey change, generate new subkey binding certificate - for (long revocation : saveParcel.revokeSubKeys) { - PGPSecretKey sKey = sKR.getSecretKey(revocation); - if (sKey == null) { - return null; + // generate and add new signature + PGPSignature sig = generateSubkeyBindingSignature(masterPublicKey, masterPrivateKey, + sKey, pKey, change.mFlags, change.mExpiry, passphrase); + pKey = PGPPublicKey.addCertification(pKey, sig); + sKR = PGPSecretKeyRing.insertSecretKey(sKR, PGPSecretKey.replacePublicKey(sKey, pKey)); } - PGPPublicKey pKey = sKey.getPublicKey(); - // generate and add new signature - PGPSignature sig = generateRevocationSignature(masterPublicKey, masterPrivateKey, pKey); - - pKey = PGPPublicKey.addCertification(pKey, sig); - sKR = PGPSecretKeyRing.insertSecretKey(sKR, PGPSecretKey.replacePublicKey(sKey, pKey)); - } - - // 5. Generate and add new subkeys - for (SaveKeyringParcel.SubkeyAdd add : saveParcel.addSubKeys) { - try { - PGPSecretKey sKey = createKey(add.mAlgorithm, add.mKeysize, passphrase, false); + // 4b. For each subkey revocation, generate new subkey revocation certificate + for (long revocation : saveParcel.revokeSubKeys) { + log.add(LogLevel.INFO, LogType.MSG_MR_SUBKEY_REVOKE, + new String[] { PgpKeyHelper.convertKeyIdToHex(revocation) }, indent); + PGPSecretKey sKey = sKR.getSecretKey(revocation); + if (sKey == null) { + log.add(LogLevel.ERROR, LogType.MSG_MR_SUBKEY_MISSING, + new String[] { PgpKeyHelper.convertKeyIdToHex(revocation) }, indent+1); + return null; + } PGPPublicKey pKey = sKey.getPublicKey(); - PGPSignature cert = generateSubkeyBindingSignature(masterPublicKey, masterPrivateKey, - sKey, pKey, add.mFlags, add.mExpiry, passphrase); - pKey = PGPPublicKey.addCertification(pKey, cert); - sKey = PGPSecretKey.replacePublicKey(sKey, pKey); + // generate and add new signature + PGPSignature sig = generateRevocationSignature(masterPublicKey, masterPrivateKey, pKey); + + pKey = PGPPublicKey.addCertification(pKey, sig); sKR = PGPSecretKeyRing.insertSecretKey(sKR, PGPSecretKey.replacePublicKey(sKey, pKey)); - } catch (PgpGeneralMsgIdException e) { - return null; } - } - // 6. If requested, change passphrase - if (saveParcel.newPassphrase != null) { - PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build() - .get(HashAlgorithmTags.SHA1); - PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( - Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); - // Build key encryptor based on new passphrase - PBESecretKeyEncryptor keyEncryptorNew = new JcePBESecretKeyEncryptorBuilder( - PGPEncryptedData.CAST5, sha1Calc) - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( - saveParcel.newPassphrase.toCharArray()); + // 5. Generate and add new subkeys + for (SaveKeyringParcel.SubkeyAdd add : saveParcel.addSubKeys) { + try { + + if (add.mExpiry != null && new Date(add.mExpiry).before(new Date())) { + log.add(LogLevel.ERROR, LogType.MSG_MR_SUBKEY_PAST_EXPIRY, indent +1); + return null; + } + + log.add(LogLevel.INFO, LogType.MSG_MR_SUBKEY_NEW, indent); + PGPSecretKey sKey = createKey(add.mAlgorithm, add.mKeysize, passphrase, false); + log.add(LogLevel.DEBUG, LogType.MSG_MR_SUBKEY_NEW_ID, + new String[] { PgpKeyHelper.convertKeyIdToHex(sKey.getKeyID()) }, indent+1); + + PGPPublicKey pKey = sKey.getPublicKey(); + PGPSignature cert = generateSubkeyBindingSignature(masterPublicKey, masterPrivateKey, + sKey, pKey, add.mFlags, add.mExpiry, passphrase); + pKey = PGPPublicKey.addCertification(pKey, cert); + sKey = PGPSecretKey.replacePublicKey(sKey, pKey); + sKR = PGPSecretKeyRing.insertSecretKey(sKR, PGPSecretKey.replacePublicKey(sKey, pKey)); + } catch (PgpGeneralMsgIdException e) { + return null; + } + } + + // 6. If requested, change passphrase + if (saveParcel.newPassphrase != null) { + log.add(LogLevel.INFO, LogType.MSG_MR_PASSPHRASE, indent); + PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build() + .get(HashAlgorithmTags.SHA1); + PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( + Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); + // Build key encryptor based on new passphrase + PBESecretKeyEncryptor keyEncryptorNew = new JcePBESecretKeyEncryptorBuilder( + PGPEncryptedData.CAST5, sha1Calc) + .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( + saveParcel.newPassphrase.toCharArray()); + + sKR = PGPSecretKeyRing.copyWithNewPassword(sKR, keyDecryptor, keyEncryptorNew); + } - sKR = PGPSecretKeyRing.copyWithNewPassword(sKR, keyDecryptor, keyEncryptorNew); + // This one must only be thrown by + } catch (IOException e) { + log.add(LogLevel.ERROR, LogType.MSG_MR_ERROR_ENCODE, indent+1); + return null; + } catch (PGPException e) { + log.add(LogLevel.ERROR, LogType.MSG_MR_ERROR_PGP, indent+1); + return null; + } catch (SignatureException e) { + log.add(LogLevel.ERROR, LogType.MSG_MR_ERROR_SIG, indent+1); + return null; } + log.add(LogLevel.OK, LogType.MSG_MR_SUCCESS, indent); return new UncachedKeyRing(sKR); } @@ -377,7 +422,7 @@ public class PgpKeyOperation { private static PGPSignature generateSubkeyBindingSignature( PGPPublicKey masterPublicKey, PGPPrivateKey masterPrivateKey, PGPSecretKey sKey, PGPPublicKey pKey, int flags, Long expiry, String passphrase) - throws PgpGeneralMsgIdException, IOException, PGPException, SignatureException { + throws IOException, PGPException, SignatureException { // date for signing Date todayDate = new Date(); @@ -414,13 +459,10 @@ public class PgpKeyOperation { if (expiry != null) { Calendar creationDate = Calendar.getInstance(TimeZone.getTimeZone("UTC")); creationDate.setTime(pKey.getCreationTime()); - // note that the below, (a/c) - (b/c) is *not* the same as (a - b) /c - // here we purposefully ignore partial days in each date - long type has - // no fractional part! - long numDays = (expiry / 86400000) - - (creationDate.getTimeInMillis() / 86400000); - if (numDays <= 0) { - throw new PgpGeneralMsgIdException(R.string.error_expiry_must_come_after_creation); + + // (Just making sure there's no programming error here, this MUST have been checked above!) + if (new Date(expiry).before(todayDate)) { + throw new RuntimeException("Bad subkey creation date, this is a bug!"); } hashedPacketsGen.setKeyExpirationTime(false, expiry - creationDate.getTimeInMillis()); } else { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java index d3f46a7a4..25a9387f4 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -52,6 +52,7 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainDatabase; import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog; import org.sufficientlysecure.keychain.util.InputData; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.ProgressScaler; @@ -486,7 +487,9 @@ public class KeychainIntentService extends IntentService String passphrase = data.getString(SAVE_KEYRING_PASSPHRASE); WrappedSecretKeyRing secRing = providerHelper.getWrappedSecretKeyRing(masterKeyId); - UncachedKeyRing ring = keyOperations.modifySecretKeyRing(secRing, saveParcel, passphrase); + OperationLog log = new OperationLog(); + UncachedKeyRing ring = keyOperations.modifySecretKeyRing(secRing, saveParcel, + passphrase, log, 0); setProgress(R.string.progress_saving_key_ring, 90, 100); providerHelper.saveSecretKeyRing(ring); } catch (ProviderHelper.NotFoundException e) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index 0760aadf8..73f552c92 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -215,6 +215,24 @@ public class OperationResultParcel implements Parcelable { MSG_KC_UID_NO_CERT (R.string.msg_kc_uid_no_cert), MSG_KC_UID_REVOKE_DUP (R.string.msg_kc_uid_revoke_dup), MSG_KC_UID_REVOKE_OLD (R.string.msg_kc_uid_revoke_old), + + MSG_MR (R.string.msg_mr), + MSG_MR_ERROR_ENCODE (R.string.msg_mr_error_encode), + MSG_MR_ERROR_PGP (R.string.msg_mr_error_pgp), + MSG_MR_ERROR_SIG (R.string.msg_mr_error_sig), + MSG_MR_PASSPHRASE (R.string.msg_mr_passphrase), + MSG_MR_SUBKEY_CHANGE (R.string.msg_mr_subkey_change), + MSG_MR_SUBKEY_MISSING (R.string.msg_mr_subkey_missing), + MSG_MR_SUBKEY_NEW_ID (R.string.msg_mr_subkey_new_id), + MSG_MR_SUBKEY_NEW (R.string.msg_mr_subkey_new), + MSG_MR_SUBKEY_PAST_EXPIRY (R.string.msg_mr_subkey_past_expiry), + MSG_MR_SUBKEY_REVOKE (R.string.msg_mr_subkey_revoke), + MSG_MR_SUCCESS (R.string.msg_mr_success), + MSG_MR_UID_ADD (R.string.msg_mr_uid_add), + MSG_MR_UID_PRIMARY (R.string.msg_mr_uid_primary), + MSG_MR_UID_REVOKE (R.string.msg_mr_uid_revoke), + MSG_MR_UNLOCK_ERROR (R.string.msg_mr_unlock_error), + MSG_MR_UNLOCK (R.string.msg_mr_unlock), ; private final int mMsgId; @@ -264,6 +282,10 @@ public class OperationResultParcel implements Parcelable { add(new OperationResultParcel.LogEntryParcel(level, type, parameters, indent)); } + public void add(LogLevel level, LogType type, int indent) { + add(new OperationResultParcel.LogEntryParcel(level, type, null, indent)); + } + public boolean containsWarnings() { for(LogEntryParcel entry : new IterableIterator(iterator())) { if (entry.mLevel == LogLevel.WARN || entry.mLevel == LogLevel.ERROR) { -- cgit v1.2.3 From 6e4d0dede1517b25dd51cdea23aa81537249c0f8 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 17 Jun 2014 23:25:45 +0200 Subject: canonicalize: support secret keys --- .../keychain/pgp/UncachedKeyRing.java | 36 ++++++++++++++-------- .../keychain/provider/ProviderHelper.java | 6 ++++ .../keychain/service/OperationResultParcel.java | 4 ++- 3 files changed, 33 insertions(+), 13 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java index f22ea7697..78620405f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java @@ -178,8 +178,7 @@ public class UncachedKeyRing { return result; } - /** "Canonicalizes" a key, removing inconsistencies in the process. This operation can be - * applied to public keyrings only. + /** "Canonicalizes" a key, removing inconsistencies in the process. * * More specifically: * - Remove all non-verifying self-certificates @@ -193,6 +192,7 @@ public class UncachedKeyRing { * - certifications and certification revocations for user ids * - If a subkey retains no valid subkey binding certificate, remove it * - If a user id retains no valid self certificate, remove it + * - If the key is a secret key, remove all certificates by foreign keys * * This operation writes an OperationLog which can be used as part of a OperationResultParcel. * @@ -200,12 +200,8 @@ public class UncachedKeyRing { * */ public UncachedKeyRing canonicalize(OperationLog log, int indent) { - if (isSecret()) { - throw new RuntimeException("Tried to canonicalize non-secret keyring. " + - "This is a programming error and should never happen!"); - } - log.add(LogLevel.START, LogType.MSG_KC, + log.add(LogLevel.START, isSecret() ? LogType.MSG_KC_SECRET : LogType.MSG_KC_PUBLIC, new String[]{PgpKeyHelper.convertKeyIdToHex(getMasterKeyId())}, indent); indent += 1; @@ -213,7 +209,7 @@ public class UncachedKeyRing { int redundantCerts = 0, badCerts = 0; - PGPPublicKeyRing ring = (PGPPublicKeyRing) mRing; + PGPKeyRing ring = mRing; PGPPublicKey masterKey = mRing.getPublicKey(); final long masterKeyId = masterKey.getKeyID(); @@ -334,8 +330,15 @@ public class UncachedKeyRing { continue; } - // If this is a foreign signature, never mind any further + // If this is a foreign signature, ... if (certId != masterKeyId) { + // never mind any further for public keys, but remove them from secret ones + if (isSecret()) { + log.add(LogLevel.WARN, LogType.MSG_KC_UID_FOREIGN, + new String[] { PgpKeyHelper.convertKeyIdToHex(certId) }, indent); + modified = PGPPublicKey.removeCertification(modified, userId, zert); + badCerts += 1; + } continue; } @@ -433,7 +436,7 @@ public class UncachedKeyRing { } // Replace modified key in the keyring - ring = PGPPublicKeyRing.insertPublicKey(ring, modified); + ring = replacePublicKey(ring, modified); indent -= 1; } @@ -578,7 +581,7 @@ public class UncachedKeyRing { // it is not properly bound? error! if (selfCert == null) { - ring = PGPPublicKeyRing.removePublicKey(ring, modified); + ring = replacePublicKey(ring, modified); log.add(LogLevel.ERROR, LogType.MSG_KC_SUB_NO_CERT, new String[]{ PgpKeyHelper.convertKeyIdToHex(key.getKeyID()) }, indent); @@ -593,7 +596,7 @@ public class UncachedKeyRing { modified = PGPPublicKey.addCertification(modified, revocation); } // replace pubkey in keyring - ring = PGPPublicKeyRing.insertPublicKey(ring, modified); + ring = replacePublicKey(ring, modified); indent -= 1; } @@ -614,5 +617,14 @@ public class UncachedKeyRing { return new UncachedKeyRing(ring); } + private static PGPKeyRing replacePublicKey(PGPKeyRing ring, PGPPublicKey key) { + if (ring instanceof PGPPublicKeyRing) { + return PGPPublicKeyRing.insertPublicKey((PGPPublicKeyRing) ring, key); + } + PGPSecretKeyRing secRing = (PGPSecretKeyRing) ring; + PGPSecretKey sKey = secRing.getSecretKey(key.getKeyID()); + sKey = PGPSecretKey.replacePublicKey(sKey, key); + return PGPSecretKeyRing.insertSecretKey(secRing, sKey); + } } 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 8688ecb6c..9573629e8 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -609,6 +609,12 @@ public class ProviderHelper { return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); } + // Canonicalize this key, to assert a number of assumptions made about it. + keyRing = keyRing.canonicalize(mLog, mIndent); + if (keyRing == null) { + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + } + long masterKeyId = keyRing.getMasterKeyId(); log(LogLevel.START, LogType.MSG_IS, new String[]{ PgpKeyHelper.convertKeyIdToHex(masterKeyId) }); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index 0760aadf8..6c75907fd 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -179,7 +179,8 @@ public class OperationResultParcel implements Parcelable { MSG_IS_SUCCESS (R.string.msg_is_success), // keyring canonicalization - MSG_KC (R.string.msg_kc), + MSG_KC_PUBLIC (R.string.msg_kc_public), + MSG_KC_SECRET (R.string.msg_kc_secret), MSG_KC_FATAL_NO_UID (R.string.msg_kc_fatal_no_uid), MSG_KC_MASTER (R.string.msg_kc_master), MSG_KC_REVOKE_BAD_ERR (R.string.msg_kc_revoke_bad_err), @@ -212,6 +213,7 @@ public class OperationResultParcel implements Parcelable { MSG_KC_UID_BAD_TYPE (R.string.msg_kc_uid_bad_type), MSG_KC_UID_BAD (R.string.msg_kc_uid_bad), MSG_KC_UID_DUP (R.string.msg_kc_uid_dup), + MSG_KC_UID_FOREIGN (R.string.msg_kc_uid_foreign), MSG_KC_UID_NO_CERT (R.string.msg_kc_uid_no_cert), MSG_KC_UID_REVOKE_DUP (R.string.msg_kc_uid_revoke_dup), MSG_KC_UID_REVOKE_OLD (R.string.msg_kc_uid_revoke_old), -- cgit v1.2.3 From 0013199b2de670b0bc3f28add5996a799525b9a2 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 17 Jun 2014 23:46:02 +0200 Subject: handle database error on secret key insert --- .../org/sufficientlysecure/keychain/provider/ProviderHelper.java | 5 ++++- .../sufficientlysecure/keychain/service/OperationResultParcel.java | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') 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 9573629e8..0218b457b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -630,7 +630,10 @@ public class ProviderHelper { values.put(KeyRingData.KEY_RING_DATA, keyRing.getEncoded()); // insert new version of this keyRing Uri uri = KeyRingData.buildSecretKeyRingUri(Long.toString(masterKeyId)); - mContentResolver.insert(uri, values); + if (mContentResolver.insert(uri, values) == null) { + log(LogLevel.ERROR, LogType.MSG_IS_DB_EXCEPTION); + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + } } catch (IOException e) { Log.e(Constants.TAG, "Failed to encode key!", e); log(LogLevel.ERROR, LogType.MSG_IS_IO_EXCPTION); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index 6c75907fd..f1f6c304a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -171,6 +171,7 @@ public class OperationResultParcel implements Parcelable { // import secret MSG_IS(R.string.msg_is), MSG_IS_BAD_TYPE_PUBLIC (R.string.msg_is_bad_type_public), + MSG_IS_DB_EXCEPTION (R.string.msg_is_db_exception), MSG_IS_IMPORTING_SUBKEYS (R.string.msg_is_importing_subkeys), MSG_IS_IO_EXCPTION (R.string.msg_is_io_excption), MSG_IS_SUBKEY_NONEXISTENT (R.string.msg_is_subkey_nonexistent), -- cgit v1.2.3 From 8c7a360d6ed6875774c912e070cb18807e0e831d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Wed, 18 Jun 2014 00:03:06 +0200 Subject: Redesign Import, still some todos and regression bugs --- .../keychain/helper/OtherHelper.java | 9 + .../keychain/ui/ImportKeysActivity.java | 190 +++++++++++---------- .../keychain/ui/ImportKeysClipboardFragment.java | 88 ---------- .../keychain/ui/ImportKeysFileFragment.java | 38 ++++- .../keychain/ui/ImportKeysKeybaseFragment.java | 16 +- .../keychain/ui/ImportKeysListFragment.java | 27 ++- .../keychain/ui/ImportKeysNFCFragment.java | 70 -------- .../keychain/ui/ImportKeysQrCodeFragment.java | 82 +++++---- .../keychain/ui/ImportKeysServerFragment.java | 34 +++- .../keychain/ui/ViewKeyActivity.java | 2 +- .../keychain/ui/adapter/ImportKeysListLoader.java | 2 +- 11 files changed, 236 insertions(+), 322 deletions(-) delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysClipboardFragment.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysNFCFragment.java (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/OtherHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/OtherHelper.java index d64587578..f04d84315 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/OtherHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/OtherHelper.java @@ -17,6 +17,7 @@ package org.sufficientlysecure.keychain.helper; +import android.content.Context; import android.os.Bundle; import android.text.SpannableStringBuilder; import android.text.Spanned; @@ -67,4 +68,12 @@ public class OtherHelper { return sb; } + public static int dpToPx(Context context, int dp) { + return (int) ((dp * context.getResources().getDisplayMetrics().density) + 0.5); + } + + public static int pxToDp(Context context, int px) { + return (int) ((px / context.getResources().getDisplayMetrics().density) + 0.5); + } + } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java index f389726ff..54186e380 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java @@ -32,10 +32,14 @@ import android.os.Messenger; import android.os.Parcelable; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentTransaction; +import android.support.v4.view.ViewPager; import android.support.v7.app.ActionBar; import android.support.v7.app.ActionBarActivity; +import android.util.DisplayMetrics; +import android.util.TypedValue; import android.view.View; import android.view.View.OnClickListener; +import android.view.ViewGroup; import android.widget.ArrayAdapter; import com.github.johnpersano.supertoasts.SuperCardToast; @@ -45,18 +49,22 @@ import com.github.johnpersano.supertoasts.util.Style; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.helper.OtherHelper; import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry; +import org.sufficientlysecure.keychain.keyimport.KeybaseKeyserver; import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; import org.sufficientlysecure.keychain.service.OperationResults.ImportResult; +import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter; +import org.sufficientlysecure.keychain.ui.widget.SlidingTabLayout; import org.sufficientlysecure.keychain.util.Log; import java.util.ArrayList; import java.util.Locale; -public class ImportKeysActivity extends ActionBarActivity implements ActionBar.OnNavigationListener { +public class ImportKeysActivity extends ActionBarActivity { public static final String ACTION_IMPORT_KEY = Constants.INTENT_PREFIX + "IMPORT_KEY"; public static final String ACTION_IMPORT_KEY_FROM_QR_CODE = Constants.INTENT_PREFIX + "IMPORT_KEY_FROM_QR_CODE"; @@ -90,23 +98,18 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O private String[] mNavigationStrings; private Fragment mCurrentFragment; private View mImportButton; + private ViewPager mViewPager; + private SlidingTabLayout mSlidingTabLayout; + private PagerTabStripAdapter mTabsAdapter; + + public static final int VIEW_PAGER_HEIGHT = 64; // dp - private static final Class[] NAVIGATION_CLASSES = new Class[]{ - ImportKeysServerFragment.class, - ImportKeysFileFragment.class, - ImportKeysQrCodeFragment.class, - ImportKeysClipboardFragment.class, - ImportKeysNFCFragment.class, - ImportKeysKeybaseFragment.class - }; private static final int NAV_SERVER = 0; - private static final int NAV_FILE = 1; - private static final int NAV_QR_CODE = 2; - private static final int NAV_CLIPBOARD = 3; - private static final int NAV_NFC = 4; - private static final int NAV_KEYBASE = 5; + private static final int NAV_QR_CODE = 1; + private static final int NAV_FILE = 2; + private static final int NAV_KEYBASE = 3; - private int mCurrentNavPosition = -1; + private int mSwitchToTab = NAV_SERVER; @Override protected void onCreate(Bundle savedInstanceState) { @@ -114,6 +117,9 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O setContentView(R.layout.import_keys_activity); + mViewPager = (ViewPager) findViewById(R.id.import_pager); + mSlidingTabLayout = (SlidingTabLayout) findViewById(R.id.import_sliding_tab_layout); + mImportButton = findViewById(R.id.import_import); mImportButton.setOnClickListener(new OnClickListener() { @Override @@ -127,18 +133,55 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O if (ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN.equals(getIntent().getAction())) { setTitle(R.string.nav_import); } else { - getSupportActionBar().setDisplayShowTitleEnabled(false); - - // set drop down navigation - Context context = getSupportActionBar().getThemedContext(); - ArrayAdapter navigationAdapter = ArrayAdapter.createFromResource(context, - R.array.import_action_list, android.R.layout.simple_spinner_dropdown_item); - getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST); - getSupportActionBar().setListNavigationCallbacks(navigationAdapter, this); + initTabs(); } handleActions(savedInstanceState, getIntent()); + } + + private void initTabs() { + mTabsAdapter = new PagerTabStripAdapter(this); + mViewPager.setAdapter(mTabsAdapter); + mSlidingTabLayout.setOnPageChangeListener(new ViewPager.OnPageChangeListener() { + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + // resize view pager back to 64 if keyserver settings have been collapsed + if (getViewPagerHeight() > VIEW_PAGER_HEIGHT) { + resizeViewPager(VIEW_PAGER_HEIGHT); + } + } + + @Override + public void onPageSelected(int position) { + } + + @Override + public void onPageScrollStateChanged(int state) { + } + }); + + Bundle serverBundle = new Bundle(); +// serverBundle.putParcelable(ViewKeyMainFragment.ARG_DATA_URI, dataUri); + mTabsAdapter.addTab(ImportKeysServerFragment.class, + serverBundle, getString(R.string.import_tab_keyserver)); + + Bundle qrCodeBundle = new Bundle(); +// importBundle.putParcelable(ViewKeyMainFragment.ARG_DATA_URI, dataUri); + mTabsAdapter.addTab(ImportKeysQrCodeFragment.class, + qrCodeBundle, getString(R.string.import_tab_qr_code)); + + Bundle fileBundle = new Bundle(); +// importBundle.putParcelable(ViewKeyMainFragment.ARG_DATA_URI, dataUri); + mTabsAdapter.addTab(ImportKeysFileFragment.class, + fileBundle, getString(R.string.import_tab_direct)); + Bundle keybaseBundle = new Bundle(); +// keybaseBundle.putParcelable(ViewKeyMainFragment.ARG_DATA_URI, dataUri); + mTabsAdapter.addTab(ImportKeysKeybaseFragment.class, + keybaseBundle, getString(R.string.import_tab_keybase)); + + // update layout after operations + mSlidingTabLayout.setViewPager(mViewPager); } protected void handleActions(Bundle savedInstanceState, Intent intent) { @@ -164,7 +207,7 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O /* Keychain's own Actions */ // display file fragment - loadNavFragment(NAV_FILE, null); + mViewPager.setCurrentItem(NAV_FILE); if (dataUri != null) { // action: directly load data @@ -199,7 +242,9 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O // display keyserver fragment with query Bundle args = new Bundle(); args.putString(ImportKeysServerFragment.ARG_QUERY, query); - loadNavFragment(NAV_SERVER, args); +// loadNavFragment(NAV_SERVER, args); + //TODO: load afterwards! + mSwitchToTab = NAV_SERVER; // action: search immediately startListFragment(savedInstanceState, null, null, query); @@ -223,9 +268,8 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O return; } } else if (ACTION_IMPORT_KEY_FROM_FILE.equals(action)) { - // NOTE: this only displays the appropriate fragment, no actions are taken - loadNavFragment(NAV_FILE, null); + mSwitchToTab = NAV_FILE; // no immediate actions! startListFragment(savedInstanceState, null, null, null); @@ -233,26 +277,28 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O // also exposed in AndroidManifest // NOTE: this only displays the appropriate fragment, no actions are taken - loadNavFragment(NAV_QR_CODE, null); + mSwitchToTab = NAV_QR_CODE; // no immediate actions! startListFragment(savedInstanceState, null, null, null); } else if (ACTION_IMPORT_KEY_FROM_NFC.equals(action)) { // NOTE: this only displays the appropriate fragment, no actions are taken - loadNavFragment(NAV_NFC, null); + mSwitchToTab = NAV_QR_CODE; // no immediate actions! startListFragment(savedInstanceState, null, null, null); } else if (ACTION_IMPORT_KEY_FROM_KEYBASE.equals(action)) { // NOTE: this only displays the appropriate fragment, no actions are taken - loadNavFragment(NAV_KEYBASE, null); + mSwitchToTab = NAV_KEYBASE; // no immediate actions! startListFragment(savedInstanceState, null, null, null); } else { startListFragment(savedInstanceState, null, null, null); } + + mViewPager.setCurrentItem(mSwitchToTab); } private void startListFragment(Bundle savedInstanceState, byte[] bytes, Uri dataUri, String serverQuery) { @@ -275,54 +321,16 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O getSupportFragmentManager().executePendingTransactions(); } - /** - * "Basically, when using a list navigation, onNavigationItemSelected() is automatically - * called when your activity is created/re-created, whether you like it or not. To prevent - * your Fragment's onCreateView() from being called twice, this initial automatic call to - * onNavigationItemSelected() should check whether the Fragment is already in existence - * inside your Activity." - *

- * from http://stackoverflow.com/a/14295474 - *

- * In our case, if we start ImportKeysActivity with parameters to directly search using a fingerprint, - * the fragment would be loaded twice resulting in the query being empty after the second load. - *

- * Our solution: - * To prevent that a fragment will be loaded again even if it was already loaded loadNavFragment - * checks against mCurrentNavPosition. - * - * @param itemPosition - * @param itemId - * @return - */ - @Override - public boolean onNavigationItemSelected(int itemPosition, long itemId) { - Log.d(Constants.TAG, "onNavigationItemSelected"); - - loadNavFragment(itemPosition, null); - - return true; - } - - private void loadNavFragment(int itemPosition, Bundle args) { - if (mCurrentNavPosition != itemPosition) { - if (ActionBar.NAVIGATION_MODE_LIST == getSupportActionBar().getNavigationMode()) { - getSupportActionBar().setSelectedNavigationItem(itemPosition); - } - loadFragment(NAVIGATION_CLASSES[itemPosition], args, mNavigationStrings[itemPosition]); - mCurrentNavPosition = itemPosition; - } + public void resizeViewPager(int dp) { + ViewGroup.LayoutParams params = mViewPager.getLayoutParams(); + params.height = OtherHelper.dpToPx(this, dp); + // update layout after operations + mSlidingTabLayout.setViewPager(mViewPager); } - private void loadFragment(Class clss, Bundle args, String tag) { - mCurrentFragment = Fragment.instantiate(this, clss.getName(), args); - - FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); - // Replace whatever is in the fragment container with this fragment - // and give the fragment a tag name equal to the string at the position selected - ft.replace(R.id.import_navigation_fragment, mCurrentFragment, tag); - // Apply changes - ft.commit(); + public int getViewPagerHeight() { + ViewGroup.LayoutParams params = mViewPager.getLayoutParams(); + return OtherHelper.pxToDp(this, params.height); } public void loadFromFingerprintUri(Bundle savedInstanceState, Uri dataUri) { @@ -349,7 +357,9 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O Bundle args = new Bundle(); args.putString(ImportKeysServerFragment.ARG_QUERY, query); args.putBoolean(ImportKeysServerFragment.ARG_DISABLE_QUERY_EDIT, true); - loadNavFragment(NAV_SERVER, args); +// loadNavFragment(NAV_SERVER, args); + + //TODO // action: search directly startListFragment(savedInstanceState, null, null, query); @@ -437,15 +447,16 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O toast.setButtonTextColor(getResources().getColor(R.color.black)); toast.setTextColor(getResources().getColor(R.color.black)); toast.setOnClickWrapper(new OnClickWrapper("supercardtoast", - new SuperToast.OnClickListener() { - @Override - public void onClick(View view, Parcelable token) { - Intent intent = new Intent( - ImportKeysActivity.this, LogDisplayActivity.class); - intent.putExtra(LogDisplayFragment.EXTRA_RESULT, result); - startActivity(intent); + new SuperToast.OnClickListener() { + @Override + public void onClick(View view, Parcelable token) { + Intent intent = new Intent( + ImportKeysActivity.this, LogDisplayActivity.class); + intent.putExtra(LogDisplayFragment.EXTRA_RESULT, result); + startActivity(intent); + } } - })); + )); toast.show(); /* @@ -560,9 +571,12 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O super.onResume(); // Check to see if the Activity started due to an Android Beam - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN - && NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) { - handleActionNdefDiscovered(getIntent()); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) { + handleActionNdefDiscovered(getIntent()); + } else { + Log.d(Constants.TAG, "NFC: No NDEF discovered!"); + } } else { Log.e(Constants.TAG, "Android Beam not supported by Android < 4.1"); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysClipboardFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysClipboardFragment.java deleted file mode 100644 index f331358fa..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysClipboardFragment.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2013-2014 Dominik Schürmann - * - * 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 . - */ - -package org.sufficientlysecure.keychain.ui; - -import android.net.Uri; -import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.view.LayoutInflater; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; - -import com.beardedhen.androidbootstrap.BootstrapButton; - -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.compatibility.ClipboardReflection; - -import java.util.Locale; - -public class ImportKeysClipboardFragment extends Fragment { - - private ImportKeysActivity mImportActivity; - private BootstrapButton mButton; - - /** - * Creates new instance of this fragment - */ - public static ImportKeysClipboardFragment newInstance() { - ImportKeysClipboardFragment frag = new ImportKeysClipboardFragment(); - - Bundle args = new Bundle(); - frag.setArguments(args); - - return frag; - } - - /** - * Inflate the layout for this fragment - */ - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.import_keys_clipboard_fragment, container, false); - - mButton = (BootstrapButton) view.findViewById(R.id.import_clipboard_button); - mButton.setOnClickListener(new OnClickListener() { - - @Override - public void onClick(View v) { - CharSequence clipboardText = ClipboardReflection.getClipboardText(getActivity()); - String sendText = ""; - if (clipboardText != null) { - sendText = clipboardText.toString(); - if (sendText.toLowerCase(Locale.ENGLISH).startsWith(Constants.FINGERPRINT_SCHEME)) { - mImportActivity.loadFromFingerprintUri(null, Uri.parse(sendText)); - return; - } - } - mImportActivity.loadCallback(sendText.getBytes(), null, null, null, null); - } - }); - - return view; - } - - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - mImportActivity = (ImportKeysActivity) getActivity(); - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java index 51f961aab..060e9bab2 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java @@ -19,21 +19,24 @@ package org.sufficientlysecure.keychain.ui; import android.app.Activity; import android.content.Intent; +import android.net.Uri; import android.os.Bundle; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import com.beardedhen.androidbootstrap.BootstrapButton; - import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.compatibility.ClipboardReflection; import org.sufficientlysecure.keychain.helper.FileHelper; +import java.util.Locale; + public class ImportKeysFileFragment extends Fragment { private ImportKeysActivity mImportActivity; - private BootstrapButton mBrowse; + private View mBrowse; + private View mClipboardButton; public static final int REQUEST_CODE_FILE = 0x00007003; @@ -56,26 +59,45 @@ public class ImportKeysFileFragment extends Fragment { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.import_keys_file_fragment, container, false); - mBrowse = (BootstrapButton) view.findViewById(R.id.import_keys_file_browse); + mBrowse = view.findViewById(R.id.import_keys_file_browse); mBrowse.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { // open .asc or .gpg files - // setting it to text/plain prevents Cynaogenmod's file manager from selecting asc + // setting it to text/plain prevents Cyanogenmod's file manager from selecting asc // or gpg types! FileHelper.openFile(ImportKeysFileFragment.this, Constants.Path.APP_DIR + "/", "*/*", REQUEST_CODE_FILE); } }); + mClipboardButton = view.findViewById(R.id.import_clipboard_button); + mClipboardButton.setOnClickListener(new View.OnClickListener() { + + @Override + public void onClick(View v) { + CharSequence clipboardText = ClipboardReflection.getClipboardText(getActivity()); + String sendText = ""; + if (clipboardText != null) { + sendText = clipboardText.toString(); + if (sendText.toLowerCase(Locale.ENGLISH).startsWith(Constants.FINGERPRINT_SCHEME)) { + mImportActivity.loadFromFingerprintUri(null, Uri.parse(sendText)); + return; + } + } + mImportActivity.loadCallback(sendText.getBytes(), null, null, null, null); + } + }); + + return view; } @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); + public void onAttach(Activity activity) { + super.onAttach(activity); - mImportActivity = (ImportKeysActivity) getActivity(); + mImportActivity = (ImportKeysActivity) activity; } @Override diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysKeybaseFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysKeybaseFragment.java index a639fe0e0..9264829ea 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysKeybaseFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysKeybaseFragment.java @@ -17,6 +17,7 @@ package org.sufficientlysecure.keychain.ui; +import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.support.v4.app.Fragment; @@ -29,8 +30,6 @@ import android.view.inputmethod.InputMethodManager; import android.widget.EditText; import android.widget.TextView; -import com.beardedhen.androidbootstrap.BootstrapButton; - import org.sufficientlysecure.keychain.R; /** @@ -40,7 +39,7 @@ import org.sufficientlysecure.keychain.R; public class ImportKeysKeybaseFragment extends Fragment { private ImportKeysActivity mImportActivity; - private BootstrapButton mSearchButton; + private View mSearchButton; private EditText mQueryEditText; public static final String ARG_QUERY = "query"; @@ -66,7 +65,7 @@ public class ImportKeysKeybaseFragment extends Fragment { mQueryEditText = (EditText) view.findViewById(R.id.import_keybase_query); - mSearchButton = (BootstrapButton) view.findViewById(R.id.import_keybase_search); + mSearchButton = view.findViewById(R.id.import_keybase_search); mSearchButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -101,8 +100,6 @@ public class ImportKeysKeybaseFragment extends Fragment { public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - mImportActivity = (ImportKeysActivity) getActivity(); - // set displayed values if (getArguments() != null) { if (getArguments().containsKey(ARG_QUERY)) { @@ -112,6 +109,13 @@ public class ImportKeysKeybaseFragment extends Fragment { } } + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + + mImportActivity = (ImportKeysActivity) activity; + } + private void search(String query) { mImportActivity.loadCallback(null, null, null, null, query); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java index d91c55da3..7d8dc4a6c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java @@ -42,6 +42,7 @@ import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListLoader; import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListServerLoader; import org.sufficientlysecure.keychain.util.InputData; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Notify; import java.io.ByteArrayInputStream; import java.io.FileNotFoundException; @@ -97,7 +98,7 @@ public class ImportKeysListFragment extends ListFragment implements public ArrayList getSelectedData() { ArrayList result = new ArrayList(); - for(ImportKeysListEntry entry : getSelectedEntries()) { + for (ImportKeysListEntry entry : getSelectedEntries()) { result.add(mCachedKeyData.get(entry.getKeyId())); } return result; @@ -273,17 +274,15 @@ public class ImportKeysListFragment extends ListFragment implements // No error mCachedKeyData = ((ImportKeysListLoader) loader).getParcelableRings(); } else if (error instanceof ImportKeysListLoader.FileHasNoContent) { - AppMsg.makeText(getActivity(), R.string.error_import_file_no_content, - AppMsg.STYLE_ALERT).show(); + Notify.showNotify(getActivity(), R.string.error_import_file_no_content, Notify.Style.ERROR); } else if (error instanceof ImportKeysListLoader.NonPgpPart) { - AppMsg.makeText(getActivity(), + Notify.showNotify(getActivity(), ((ImportKeysListLoader.NonPgpPart) error).getCount() + " " + getResources(). getQuantityString(R.plurals.error_import_non_pgp_part, ((ImportKeysListLoader.NonPgpPart) error).getCount()), - new AppMsg.Style(AppMsg.LENGTH_LONG, R.color.confirm)).show(); + Notify.Style.OK); } else { - AppMsg.makeText(getActivity(), R.string.error_generic_report_bug, - new AppMsg.Style(AppMsg.LENGTH_LONG, R.color.alert)).show(); + Notify.showNotify(getActivity(), R.string.error_generic_report_bug, Notify.Style.ERROR); } break; @@ -292,23 +291,17 @@ public class ImportKeysListFragment extends ListFragment implements // TODO: possibly fine-tune message building for these two cases if (error == null) { - AppMsg.makeText( - getActivity(), getResources().getQuantityString(R.plurals.keys_found, - mAdapter.getCount(), mAdapter.getCount()), - AppMsg.STYLE_INFO - ).show(); + // No error } else if (error instanceof Keyserver.QueryTooShortException) { - AppMsg.makeText(getActivity(), R.string.error_keyserver_insufficient_query, - AppMsg.STYLE_ALERT).show(); + Notify.showNotify(getActivity(), R.string.error_keyserver_insufficient_query, Notify.Style.ERROR); } else if (error instanceof Keyserver.TooManyResponsesException) { - AppMsg.makeText(getActivity(), R.string.error_keyserver_too_many_responses, - AppMsg.STYLE_ALERT).show(); + Notify.showNotify(getActivity(), R.string.error_keyserver_too_many_responses, Notify.Style.ERROR); } else if (error instanceof Keyserver.QueryFailedException) { Log.d(Constants.TAG, "Unrecoverable keyserver query error: " + error.getLocalizedMessage()); String alert = getActivity().getString(R.string.error_searching_keys); alert = alert + " (" + error.getLocalizedMessage() + ")"; - AppMsg.makeText(getActivity(), alert, AppMsg.STYLE_ALERT).show(); + Notify.showNotify(getActivity(), alert, Notify.Style.ERROR); } break; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysNFCFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysNFCFragment.java deleted file mode 100644 index 45f464b1c..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysNFCFragment.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2013-2014 Dominik Schürmann - * - * 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 . - */ - -package org.sufficientlysecure.keychain.ui; - -import android.content.Intent; -import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.view.LayoutInflater; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; - -import com.beardedhen.androidbootstrap.BootstrapButton; - -import org.sufficientlysecure.keychain.R; - -public class ImportKeysNFCFragment extends Fragment { - - private BootstrapButton mButton; - - /** - * Creates new instance of this fragment - */ - public static ImportKeysNFCFragment newInstance() { - ImportKeysNFCFragment frag = new ImportKeysNFCFragment(); - - Bundle args = new Bundle(); - frag.setArguments(args); - - return frag; - } - - /** - * Inflate the layout for this fragment - */ - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.import_keys_nfc_fragment, container, false); - - mButton = (BootstrapButton) view.findViewById(R.id.import_nfc_button); - mButton.setOnClickListener(new OnClickListener() { - - @Override - public void onClick(View v) { - // show nfc help - Intent intent = new Intent(getActivity(), HelpActivity.class); - intent.putExtra(HelpActivity.EXTRA_SELECTED_TAB, HelpActivity.TAB_NFC); - startActivityForResult(intent, 0); - } - }); - - return view; - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java index 22b56e1ab..0cbb51c77 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java @@ -17,47 +17,47 @@ package org.sufficientlysecure.keychain.ui; +import android.app.Activity; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; -import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.ProgressBar; import android.widget.TextView; -import com.beardedhen.androidbootstrap.BootstrapButton; -import com.devspark.appmsg.AppMsg; import com.google.zxing.integration.android.IntentResult; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.util.IntentIntegratorSupportV4; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Notify; import java.util.ArrayList; import java.util.Locale; public class ImportKeysQrCodeFragment extends Fragment { - private ImportKeysActivity mImportActivity; - private BootstrapButton mButton; - private TextView mText; - private ProgressBar mProgress; + private View mNfcButton; + + private View mQrCodeButton; + private TextView mQrCodeText; + private ProgressBar mQrCodeProgress; - private String[] mScannedContent; + private String[] mQrCodeContent; /** * Creates new instance of this fragment */ - public static ImportKeysQrCodeFragment newInstance() { - ImportKeysQrCodeFragment frag = new ImportKeysQrCodeFragment(); + public static ImportKeysFileFragment newInstance() { + ImportKeysFileFragment frag = new ImportKeysFileFragment(); Bundle args = new Bundle(); - frag.setArguments(args); + frag.setArguments(args); return frag; } @@ -68,11 +68,23 @@ public class ImportKeysQrCodeFragment extends Fragment { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.import_keys_qr_code_fragment, container, false); - mButton = (BootstrapButton) view.findViewById(R.id.import_qrcode_button); - mText = (TextView) view.findViewById(R.id.import_qrcode_text); - mProgress = (ProgressBar) view.findViewById(R.id.import_qrcode_progress); + mNfcButton = view.findViewById(R.id.import_nfc_button); + mNfcButton.setOnClickListener(new View.OnClickListener() { + + @Override + public void onClick(View v) { + // show nfc help + Intent intent = new Intent(getActivity(), HelpActivity.class); + intent.putExtra(HelpActivity.EXTRA_SELECTED_TAB, HelpActivity.TAB_NFC); + startActivityForResult(intent, 0); + } + }); + + mQrCodeButton = view.findViewById(R.id.import_qrcode_button); + mQrCodeText = (TextView) view.findViewById(R.id.import_qrcode_text); + mQrCodeProgress = (ProgressBar) view.findViewById(R.id.import_qrcode_progress); - mButton.setOnClickListener(new OnClickListener() { + mQrCodeButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -85,10 +97,10 @@ public class ImportKeysQrCodeFragment extends Fragment { } @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); + public void onAttach(Activity activity) { + super.onAttach(activity); - mImportActivity = (ImportKeysActivity) getActivity(); + mImportActivity = (ImportKeysActivity) activity; } @Override @@ -122,8 +134,7 @@ public class ImportKeysQrCodeFragment extends Fragment { } // fail... - AppMsg.makeText(getActivity(), R.string.import_qr_code_wrong, AppMsg.STYLE_ALERT) - .show(); + Notify.showNotify(getActivity(), R.string.import_qr_code_wrong, Notify.Style.ERROR); } break; @@ -136,6 +147,7 @@ public class ImportKeysQrCodeFragment extends Fragment { } } + public void importFingerprint(Uri dataUri) { mImportActivity.loadFromFingerprintUri(null, dataUri); } @@ -151,32 +163,31 @@ public class ImportKeysQrCodeFragment extends Fragment { // first qr code -> setup if (counter == 0) { - mScannedContent = new String[size]; - mProgress.setMax(size); - mProgress.setVisibility(View.VISIBLE); - mText.setVisibility(View.VISIBLE); + mQrCodeContent = new String[size]; + mQrCodeProgress.setMax(size); + mQrCodeProgress.setVisibility(View.VISIBLE); + mQrCodeText.setVisibility(View.VISIBLE); } - if (mScannedContent == null || counter > mScannedContent.length) { - AppMsg.makeText(getActivity(), R.string.import_qr_code_start_with_one, AppMsg.STYLE_ALERT) - .show(); + if (mQrCodeContent == null || counter > mQrCodeContent.length) { + Notify.showNotify(getActivity(), R.string.import_qr_code_start_with_one, Notify.Style.ERROR); return; } // save scanned content - mScannedContent[counter] = content; + mQrCodeContent[counter] = content; // get missing numbers ArrayList missing = new ArrayList(); - for (int i = 0; i < mScannedContent.length; i++) { - if (mScannedContent[i] == null) { + for (int i = 0; i < mQrCodeContent.length; i++) { + if (mQrCodeContent[i] == null) { missing.add(i); } } // update progress and text - int alreadyScanned = mScannedContent.length - missing.size(); - mProgress.setProgress(alreadyScanned); + int alreadyScanned = mQrCodeContent.length - missing.size(); + mQrCodeProgress.setProgress(alreadyScanned); String missingString = ""; for (int m : missing) { @@ -188,17 +199,16 @@ public class ImportKeysQrCodeFragment extends Fragment { String missingText = getResources().getQuantityString(R.plurals.import_qr_code_missing, missing.size(), missingString); - mText.setText(missingText); + mQrCodeText.setText(missingText); // finished! if (missing.size() == 0) { - mText.setText(R.string.import_qr_code_finished); + mQrCodeText.setText(R.string.import_qr_code_finished); String result = ""; - for (String in : mScannedContent) { + for (String in : mQrCodeContent) { result += in; } mImportActivity.loadCallback(result.getBytes(), null, null, null, null); } } - } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java index 9e3d88ff5..eabc8348c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java @@ -17,6 +17,7 @@ package org.sufficientlysecure.keychain.ui; +import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.support.v4.app.Fragment; @@ -32,8 +33,6 @@ import android.widget.EditText; import android.widget.Spinner; import android.widget.TextView; -import com.beardedhen.androidbootstrap.BootstrapButton; - import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.helper.Preferences; @@ -46,8 +45,10 @@ public class ImportKeysServerFragment extends Fragment { private ImportKeysActivity mImportActivity; - private BootstrapButton mSearchButton; + private View mSearchButton; private EditText mQueryEditText; + private View mConfigButton; + private View mConfigLayout; private Spinner mServerSpinner; private ArrayAdapter mServerAdapter; @@ -73,14 +74,17 @@ public class ImportKeysServerFragment extends Fragment { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.import_keys_server_fragment, container, false); - mSearchButton = (BootstrapButton) view.findViewById(R.id.import_server_search); + mSearchButton = view.findViewById(R.id.import_server_search); mQueryEditText = (EditText) view.findViewById(R.id.import_server_query); + mConfigButton = view.findViewById(R.id.import_server_config_button); + mConfigLayout = view.findViewById(R.id.import_server_config); mServerSpinner = (Spinner) view.findViewById(R.id.import_server_spinner); // add keyservers to spinner mServerAdapter = new ArrayAdapter(getActivity(), android.R.layout.simple_spinner_item, Preferences.getPreferences(getActivity()) - .getKeyServers()); + .getKeyServers() + ); mServerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); mServerSpinner.setAdapter(mServerAdapter); if (mServerAdapter.getCount() > 0) { @@ -118,6 +122,17 @@ public class ImportKeysServerFragment extends Fragment { } }); + mConfigButton.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + if (mImportActivity.getViewPagerHeight() > ImportKeysActivity.VIEW_PAGER_HEIGHT) { + mImportActivity.resizeViewPager(ImportKeysActivity.VIEW_PAGER_HEIGHT); + } else { + mImportActivity.resizeViewPager(ImportKeysActivity.VIEW_PAGER_HEIGHT + 41); + } + } + }); + return view; } @@ -125,8 +140,6 @@ public class ImportKeysServerFragment extends Fragment { public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - mImportActivity = (ImportKeysActivity) getActivity(); - // set displayed values if (getArguments() != null) { if (getArguments().containsKey(ARG_QUERY)) { @@ -150,6 +163,13 @@ public class ImportKeysServerFragment extends Fragment { } } + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + + mImportActivity = (ImportKeysActivity) activity; + } + private void search(String query, String keyServer) { mImportActivity.loadCallback(null, null, query, keyServer, null); } 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 463c800d2..c3ca0334f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -158,7 +158,7 @@ public class ViewKeyActivity extends ActionBarActivity implements Bundle shareBundle = new Bundle(); shareBundle.putParcelable(ViewKeyMainFragment.ARG_DATA_URI, dataUri); mTabsAdapter.addTab(ViewKeyShareFragment.class, - mainBundle, getString(R.string.key_view_tab_share)); + shareBundle, getString(R.string.key_view_tab_share)); // update layout after operations mSlidingTabLayout.setViewPager(mViewPager); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java index 03a82696d..c2712e89e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java @@ -133,7 +133,7 @@ public class ImportKeysListLoader // read all available blocks... (asc files can contain many blocks with BEGIN END) while (bufferedInput.available() > 0) { - // todo deal with non-keyring objects? + // TODO: deal with non-keyring objects? List rings = UncachedKeyRing.fromStream(bufferedInput); for(UncachedKeyRing key : rings) { ImportKeysListEntry item = new ImportKeysListEntry(getContext(), key); -- cgit v1.2.3 From c63f3c8a5d0a3e5c0c0587ae3448e014878930c0 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Wed, 18 Jun 2014 00:11:43 +0200 Subject: prepare ProviderHelper for consolidation before insert --- .../keychain/provider/ProviderHelper.java | 158 +++++++++++---------- .../keychain/service/KeychainIntentService.java | 4 +- 2 files changed, 87 insertions(+), 75 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') 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 0218b457b..1bc47fa7c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -187,9 +187,9 @@ public class ProviderHelper { KeyRings.PUBKEY_DATA }, KeyRings.HAS_ANY_SECRET + " = 1", null, null); - LongSparseArray result = - new LongSparseArray(cursor.getCount()); try { + LongSparseArray result = new LongSparseArray(); + if (cursor != null && cursor.moveToFirst()) do { long masterKeyId = cursor.getLong(0); boolean hasAnySecret = cursor.getInt(1) > 0; @@ -200,13 +200,15 @@ public class ProviderHelper { new WrappedPublicKeyRing(blob, hasAnySecret, verified).getSubkey()); } } while (cursor.moveToNext()); + + return result; + } finally { if (cursor != null) { cursor.close(); } } - return result; } public CachedPublicKeyRing getCachedPublicKeyRing(Uri queryUri) { @@ -260,32 +262,18 @@ public class ProviderHelper { } } - public SaveKeyringResult savePublicKeyRing(UncachedKeyRing keyRing) { - return savePublicKeyRing(keyRing, new Progressable() { - @Override - public void setProgress(String message, int current, int total) { - return; - } - - @Override - public void setProgress(int resourceId, int current, int total) { - return; - } - - @Override - public void setProgress(int current, int total) { - return; - } - }); - } - /** - * Saves PGPPublicKeyRing with its keys and userIds in DB + /** Saves an UncachedKeyRing of the public variant into the db. + * + * This method will not delete all previous data for this masterKeyId from the database prior + * to inserting. All public data is effectively re-inserted, secret keyrings are left deleted + * and need to be saved externally to be preserved past the operation. */ @SuppressWarnings("unchecked") - public SaveKeyringResult savePublicKeyRing(UncachedKeyRing keyRing, Progressable progress) { + private int internalSavePublicKeyRing(UncachedKeyRing keyRing, + Progressable progress, boolean selfCertsAreTrusted) { if (keyRing.isSecret()) { log(LogLevel.ERROR, LogType.MSG_IP_BAD_TYPE_SECRET); - return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + return SaveKeyringResult.RESULT_ERROR; } // start with ok result @@ -299,21 +287,11 @@ public class ProviderHelper { // Canonicalize this key, to assert a number of assumptions made about it. keyRing = keyRing.canonicalize(mLog, mIndent); if (keyRing == null) { - return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + return SaveKeyringResult.RESULT_ERROR; } UncachedPublicKey masterKey = keyRing.getPublicKey(); - // IF there is a secret key, preserve it! - UncachedKeyRing secretRing; - try { - secretRing = getWrappedSecretKeyRing(masterKeyId).getUncached(); - log(LogLevel.DEBUG, LogType.MSG_IP_PRESERVING_SECRET); - progress.setProgress(LogType.MSG_IP_PRESERVING_SECRET.getMsgId(), 30, 100); - } catch (NotFoundException e) { - secretRing = null; - } - ArrayList operations; try { @@ -331,7 +309,7 @@ public class ProviderHelper { values.put(KeyRingData.KEY_RING_DATA, keyRing.getEncoded()); } catch (IOException e) { log(LogLevel.ERROR, LogType.MSG_IP_ENCODE_FAIL); - return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + return SaveKeyringResult.RESULT_ERROR; } Uri uri = KeyRingData.buildPublicKeyRingUri(Long.toString(masterKeyId)); @@ -500,7 +478,7 @@ public class ProviderHelper { } mIndent -= 1; - progress.setProgress(LogType.MSG_IP_UID_REORDER.getMsgId(), 80, 100); + progress.setProgress(LogType.MSG_IP_UID_REORDER.getMsgId(), 65, 100); log(LogLevel.DEBUG, LogType.MSG_IP_UID_REORDER); // primary before regular before revoked (see UserIdItem.compareTo) // this is a stable sort, so the order of keys is otherwise preserved. @@ -511,7 +489,7 @@ public class ProviderHelper { operations.add(buildUserIdOperations(masterKeyId, item, userIdRank)); if (item.selfCert != null) { operations.add(buildCertOperations(masterKeyId, userIdRank, item.selfCert, - secretRing != null ? Certs.VERIFIED_SECRET : Certs.VERIFIED_SELF)); + selfCertsAreTrusted ? Certs.VERIFIED_SECRET : Certs.VERIFIED_SELF)); } // don't bother with trusted certs if the uid is revoked, anyways if (item.isRevoked) { @@ -529,7 +507,7 @@ public class ProviderHelper { log(LogLevel.ERROR, LogType.MSG_IP_FAIL_IO_EXC); Log.e(Constants.TAG, "IOException during import", e); mIndent -= 1; - return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + return SaveKeyringResult.RESULT_ERROR; } try { @@ -544,33 +522,24 @@ public class ProviderHelper { } log(LogLevel.DEBUG, LogType.MSG_IP_APPLY_BATCH); - progress.setProgress(LogType.MSG_IP_APPLY_BATCH.getMsgId(), 90, 100); + progress.setProgress(LogType.MSG_IP_APPLY_BATCH.getMsgId(), 75, 100); mContentResolver.applyBatch(KeychainContract.CONTENT_AUTHORITY, operations); - // Save the saved keyring (if any) - if (secretRing != null) { - log(LogLevel.DEBUG, LogType.MSG_IP_REINSERT_SECRET); - mIndent += 1; - saveSecretKeyRing(secretRing); - result |= SaveKeyringResult.SAVED_SECRET; - mIndent -= 1; - } - - mIndent -= 1; log(LogLevel.OK, LogType.MSG_IP_SUCCESS); - progress.setProgress(LogType.MSG_IP_SUCCESS.getMsgId(), 100, 100); - return new SaveKeyringResult(result, mLog); + mIndent -= 1; + progress.setProgress(LogType.MSG_IP_SUCCESS.getMsgId(), 90, 100); + return result; } catch (RemoteException e) { log(LogLevel.ERROR, LogType.MSG_IP_FAIL_REMOTE_EX); Log.e(Constants.TAG, "RemoteException during import", e); mIndent -= 1; - return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + return SaveKeyringResult.RESULT_ERROR; } catch (OperationApplicationException e) { log(LogLevel.ERROR, LogType.MSG_IP_FAIL_OP_EX); Log.e(Constants.TAG, "OperationApplicationException during import", e); mIndent -= 1; - return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + return SaveKeyringResult.RESULT_ERROR; } } @@ -596,30 +565,27 @@ public class ProviderHelper { } } - /** - * Saves a PGPSecretKeyRing in the DB. This will only work if a corresponding public keyring - * is already in the database! - * - * TODO allow adding secret keys where no public key exists (ie, consolidate keys) + /** Saves an UncachedKeyRing of the secret variant into the db. + * This method will fail if no corresponding public keyring is in the database! */ - public SaveKeyringResult saveSecretKeyRing(UncachedKeyRing keyRing) { + private SaveKeyringResult internalSaveSecretKeyRing(UncachedKeyRing keyRing) { if (!keyRing.isSecret()) { log(LogLevel.ERROR, LogType.MSG_IS_BAD_TYPE_PUBLIC); return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); } + long masterKeyId = keyRing.getMasterKeyId(); + log(LogLevel.START, LogType.MSG_IS, + new String[]{ PgpKeyHelper.convertKeyIdToHex(masterKeyId) }); + mIndent += 1; + // Canonicalize this key, to assert a number of assumptions made about it. keyRing = keyRing.canonicalize(mLog, mIndent); if (keyRing == null) { return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); } - long masterKeyId = keyRing.getMasterKeyId(); - log(LogLevel.START, LogType.MSG_IS, - new String[]{ PgpKeyHelper.convertKeyIdToHex(masterKeyId) }); - mIndent += 1; - // IF this is successful, it's a secret key int result = SaveKeyringResult.SAVED_SECRET; @@ -685,18 +651,67 @@ public class ProviderHelper { } + + public SaveKeyringResult savePublicKeyRing(UncachedKeyRing keyRing) { + return savePublicKeyRing(keyRing, new Progressable() { + @Override + public void setProgress(String message, int current, int total) { + } + + @Override + public void setProgress(int resourceId, int current, int total) { + } + + @Override + public void setProgress(int current, int total) { + } + }); + } + + public SaveKeyringResult savePublicKeyRing(UncachedKeyRing keyRing, Progressable progress) { + + // IF there is a secret key, preserve it! + UncachedKeyRing secretRing; + try { + secretRing = getWrappedSecretKeyRing(keyRing.getMasterKeyId()).getUncached(); + log(LogLevel.DEBUG, LogType.MSG_IP_PRESERVING_SECRET); + progress.setProgress(LogType.MSG_IP_PRESERVING_SECRET.getMsgId(), 10, 100); + } catch (NotFoundException e) { + secretRing = null; + } + + int result = internalSavePublicKeyRing(keyRing, progress, secretRing != null); + + // Save the saved keyring (if any) + if (secretRing != null) { + log(LogLevel.DEBUG, LogType.MSG_IP_REINSERT_SECRET); + progress.setProgress(LogType.MSG_IP_REINSERT_SECRET.getMsgId(), 90, 100); + mIndent += 1; + internalSaveSecretKeyRing(secretRing); + result |= SaveKeyringResult.SAVED_SECRET; + mIndent -= 1; + } + + return new SaveKeyringResult(result, mLog); + + } + + public SaveKeyringResult saveSecretKeyRing(UncachedKeyRing keyRing) { + return internalSaveSecretKeyRing(keyRing); + } + /** * Saves (or updates) a pair of public and secret KeyRings in the database */ - public void saveKeyRing(UncachedKeyRing pubRing, UncachedKeyRing secRing) throws IOException { - long masterKeyId = pubRing.getPublicKey().getKeyId(); + public void savePairedKeyRing(UncachedKeyRing pubRing, UncachedKeyRing secRing) throws IOException { + long masterKeyId = pubRing.getMasterKeyId(); // delete secret keyring (so it isn't unnecessarily saved by public-savePublicKeyRing below) mContentResolver.delete(KeyRingData.buildSecretKeyRingUri(Long.toString(masterKeyId)), null, null); // save public keyring - savePublicKeyRing(pubRing); - saveSecretKeyRing(secRing); + internalSavePublicKeyRing(pubRing, null, true); + internalSaveSecretKeyRing(secRing); } /** @@ -812,9 +827,6 @@ public class ProviderHelper { /** * Must be an uri pointing to an account - * - * @param uri - * @return */ public AppSettings getApiAppSettings(Uri uri) { AppSettings settings = null; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java index 3ddcdfcf4..e30d48ce5 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -524,13 +524,13 @@ public class KeychainIntentService extends IntentService PgpKeyOperation.Pair pair = keyOperations.buildSecretKey(seckey, pubkey, saveParcel); // edit existing setProgress(R.string.progress_saving_key_ring, 90, 100); - providerHelper.saveKeyRing(pair.first, pair.second); + providerHelper.savePairedKeyRing(pair.first, pair.second); } catch (ProviderHelper.NotFoundException e) { PgpKeyOperation.Pair pair = keyOperations.buildNewSecretKey(saveParcel); //new Keyring // save the pair setProgress(R.string.progress_saving_key_ring, 90, 100); - providerHelper.saveKeyRing(pair.first, pair.second); + providerHelper.savePairedKeyRing(pair.first, pair.second); } setProgress(R.string.progress_done, 100, 100); -- cgit v1.2.3 From adf15d4d16cd6e9943f535f2beed63b72ec5e6b4 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Wed, 18 Jun 2014 00:25:52 +0200 Subject: import-log: don't count self certs as unknown --- .../java/org/sufficientlysecure/keychain/provider/ProviderHelper.java | 1 + 1 file changed, 1 insertion(+) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') 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 1bc47fa7c..dbcc4a1c6 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -441,6 +441,7 @@ public class ProviderHelper { item.isRevoked = true; log(LogLevel.INFO, LogType.MSG_IP_UID_REVOKED); } + continue; } -- cgit v1.2.3 From b4974d922e0a5472932e211d6a87b3ea901dfae6 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Mon, 16 Jun 2014 23:52:06 +0200 Subject: consolidate: first draft --- .../keychain/pgp/UncachedKeyRing.java | 117 ++++++++++++++++++++- .../keychain/provider/ProviderHelper.java | 2 +- 2 files changed, 115 insertions(+), 4 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java index 78620405f..5f1c2cac3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java @@ -27,6 +27,7 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.Arrays; import java.util.Date; import java.util.HashSet; import java.util.Iterator; @@ -178,7 +179,8 @@ public class UncachedKeyRing { return result; } - /** "Canonicalizes" a key, removing inconsistencies in the process. + /** "Canonicalizes" a public key, removing inconsistencies in the process. This variant can be + * applied to public keyrings only. * * More specifically: * - Remove all non-verifying self-certificates @@ -193,13 +195,18 @@ public class UncachedKeyRing { * - If a subkey retains no valid subkey binding certificate, remove it * - If a user id retains no valid self certificate, remove it * - If the key is a secret key, remove all certificates by foreign keys + * - If no valid user id remains, log an error and return null * * This operation writes an OperationLog which can be used as part of a OperationResultParcel. * - * @return A canonicalized key + * @return A canonicalized key, or null on fatal error * */ - public UncachedKeyRing canonicalize(OperationLog log, int indent) { + public UncachedKeyRing canonicalizePublic(OperationLog log, int indent) { + if (isSecret()) { + throw new RuntimeException("Tried to public-canonicalize non-public keyring. " + + "This is a programming error and should never happen!"); + } log.add(LogLevel.START, isSecret() ? LogType.MSG_KC_SECRET : LogType.MSG_KC_PUBLIC, new String[]{PgpKeyHelper.convertKeyIdToHex(getMasterKeyId())}, indent); @@ -627,4 +634,108 @@ public class UncachedKeyRing { return PGPSecretKeyRing.insertSecretKey(secRing, sKey); } + /** This operation consolidates a list of UncachedKeyRings into a single, combined + * UncachedKeyRing. + * + * The combined keyring contains the subkeys and user ids of all input keyrings. Even if all + * input keyrings were canonicalized at some point, the resulting keyring will not necessarily + * have that property. + * + * TODO work with secret keys + * + * @return A consolidated UncachedKeyRing with the data of all input keyrings. + * + */ + public static UncachedKeyRing consolidate(List list, + OperationLog log, int indent) { + + long masterKeyId = list.get(0).getMasterKeyId(); + for (UncachedKeyRing ring : new IterableIterator(list.iterator())) { + if (ring.getMasterKeyId() != masterKeyId) { + // log.add(LogLevel.ERROR, LogType.MSG_KO, null, indent); + return null; + } + } + + // log.add(LogLevel.START, LogType.MSG_KO, + // new String[]{PgpKeyHelper.convertKeyIdToHex(masterKeyId)}, indent); + indent += 1; + + // remember which certs we already added + HashSet certs = new HashSet(); + + try { + PGPPublicKeyRing result = null; + for (UncachedKeyRing uring : new IterableIterator(list.iterator())) { + PGPPublicKeyRing ring = (PGPPublicKeyRing) uring.mRing; + if (result == null) { + result = ring; + continue; + } + + for (PGPPublicKey key : new IterableIterator(ring.getPublicKeys())) { + + final PGPPublicKey resultkey = result.getPublicKey(key.getKeyID()); + if (resultkey == null) { + log.add(LogLevel.DEBUG, LogType.MSG_KO_NEW_SUBKEY, null, indent); + result = PGPPublicKeyRing.insertPublicKey(result, key); + continue; + } + + // The key old key, which we merge stuff into + PGPPublicKey modified = resultkey; + for (PGPSignature cert : new IterableIterator(key.getSignatures())) { + int type = cert.getSignatureType(); + // Disregard certifications on user ids, we will deal with those later + if (type == PGPSignature.NO_CERTIFICATION + || type == PGPSignature.DEFAULT_CERTIFICATION + || type == PGPSignature.CASUAL_CERTIFICATION + || type == PGPSignature.POSITIVE_CERTIFICATION + || type == PGPSignature.CERTIFICATION_REVOCATION) { + continue; + } + + int hash = Arrays.hashCode(cert.getEncoded()); + // Known cert, skip it + if (certs.contains(hash)) { + continue; + } + certs.add(hash); + modified = PGPPublicKey.addCertification(modified, cert); + } + + // If this is a subkey, stop here + if (!key.isMasterKey()) { + result = PGPPublicKeyRing.insertPublicKey(result, modified); + continue; + } + + // Copy over all user id certificates + for (String userId : new IterableIterator(key.getUserIDs())) { + for (PGPSignature cert : new IterableIterator(key.getSignaturesForID(userId))) { + int hash = Arrays.hashCode(cert.getEncoded()); + // Known cert, skip it + if (certs.contains(hash)) { + continue; + } + certs.add(hash); + modified = PGPPublicKey.addCertification(modified, userId, cert); + } + } + // If anything changed, save the updated (sub)key + if (modified != resultkey) { + result = PGPPublicKeyRing.insertPublicKey(result, modified); + } + } + + } + + return new UncachedKeyRing(result); + + } catch (IOException e) { + return 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 dbcc4a1c6..22454b20f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -285,7 +285,7 @@ public class ProviderHelper { mIndent += 1; // Canonicalize this key, to assert a number of assumptions made about it. - keyRing = keyRing.canonicalize(mLog, mIndent); + keyRing = keyRing.canonicalizePublic(mLog, mIndent); if (keyRing == null) { return SaveKeyringResult.RESULT_ERROR; } -- cgit v1.2.3 From d8b0015d25b12f1b82e1a044fb3b130844141fda Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 17 Jun 2014 00:12:06 +0200 Subject: consolidate: add logging --- .../keychain/pgp/UncachedKeyRing.java | 37 +++++++++++++++++----- .../keychain/pgp/WrappedKeyRing.java | 6 ++++ .../keychain/provider/ProviderHelper.java | 8 +++++ .../keychain/service/OperationResultParcel.java | 11 +++++++ 4 files changed, 54 insertions(+), 8 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java index 5f1c2cac3..5cd13c4a2 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java @@ -643,6 +643,7 @@ public class UncachedKeyRing { * * TODO work with secret keys * + * @param list The list of UncachedKeyRings. Must not be empty, and all of the same masterKeyId * @return A consolidated UncachedKeyRing with the data of all input keyrings. * */ @@ -650,15 +651,12 @@ public class UncachedKeyRing { OperationLog log, int indent) { long masterKeyId = list.get(0).getMasterKeyId(); - for (UncachedKeyRing ring : new IterableIterator(list.iterator())) { - if (ring.getMasterKeyId() != masterKeyId) { - // log.add(LogLevel.ERROR, LogType.MSG_KO, null, indent); - return null; - } - } - // log.add(LogLevel.START, LogType.MSG_KO, - // new String[]{PgpKeyHelper.convertKeyIdToHex(masterKeyId)}, indent); + log.add(LogLevel.START, LogType.MSG_KO, + new String[]{ + Integer.toString(list.size()), + PgpKeyHelper.convertKeyIdToHex(masterKeyId) + }, indent); indent += 1; // remember which certs we already added @@ -666,13 +664,28 @@ public class UncachedKeyRing { try { PGPPublicKeyRing result = null; + int num = 1; for (UncachedKeyRing uring : new IterableIterator(list.iterator())) { + PGPPublicKeyRing ring = (PGPPublicKeyRing) uring.mRing; + if (uring.getMasterKeyId() != masterKeyId) { + log.add(LogLevel.ERROR, LogType.MSG_KO_HETEROGENEOUS, null, indent); + return null; + } + + // If this is the first ring, just take it if (result == null) { result = ring; continue; } + log.add(LogLevel.DEBUG, LogType.MSG_KO_MERGING, + new String[] { Integer.toString(num++) }, indent); + indent += 1; + + // keep track of the number of new certs we add + int newCerts = 0; + for (PGPPublicKey key : new IterableIterator(ring.getPublicKeys())) { final PGPPublicKey resultkey = result.getPublicKey(key.getKeyID()); @@ -702,6 +715,7 @@ public class UncachedKeyRing { } certs.add(hash); modified = PGPPublicKey.addCertification(modified, cert); + newCerts += 1; } // If this is a subkey, stop here @@ -718,6 +732,7 @@ public class UncachedKeyRing { if (certs.contains(hash)) { continue; } + newCerts += 1; certs.add(hash); modified = PGPPublicKey.addCertification(modified, userId, cert); } @@ -726,13 +741,19 @@ public class UncachedKeyRing { if (modified != resultkey) { result = PGPPublicKeyRing.insertPublicKey(result, modified); } + } + log.add(LogLevel.DEBUG, LogType.MSG_KO_FOUND_NEW, + new String[] { Integer.toString(newCerts) }, indent); + + } return new UncachedKeyRing(result); } catch (IOException e) { + log.add(LogLevel.ERROR, LogType.MSG_KO_FATAL_ENCODE, null, indent); return null; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedKeyRing.java index 71d237c05..632a04fc3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedKeyRing.java @@ -91,6 +91,12 @@ public abstract class WrappedKeyRing extends KeyRing { getRing().encode(stream); } + /** Returns an UncachedKeyRing which wraps the same data as this ring. This method should + * only be used */ + public UncachedKeyRing getUncachedKeyRing() { + return new UncachedKeyRing(getRing()); + } + abstract PGPKeyRing getRing(); abstract public IterableIterator publicKeyIterator(); 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 22454b20f..cbdf6a64b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -284,6 +284,14 @@ public class ProviderHelper { new String[]{ PgpKeyHelper.convertKeyIdToHex(masterKeyId) }); mIndent += 1; + try { + WrappedPublicKeyRing ring = getWrappedPublicKeyRing(KeyRings.buildUnifiedKeyRingUri(masterKeyId)); + // ring.get + + } catch(NotFoundException e) { + // no biggie + } + // Canonicalize this key, to assert a number of assumptions made about it. keyRing = keyRing.canonicalizePublic(mLog, mIndent); if (keyRing == null) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index f1f6c304a..408715fdf 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -3,8 +3,10 @@ package org.sufficientlysecure.keychain.service; import android.os.Parcel; import android.os.Parcelable; +import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.util.IterableIterator; +import org.sufficientlysecure.keychain.util.Log; import java.util.ArrayList; @@ -218,6 +220,14 @@ public class OperationResultParcel implements Parcelable { MSG_KC_UID_NO_CERT (R.string.msg_kc_uid_no_cert), MSG_KC_UID_REVOKE_DUP (R.string.msg_kc_uid_revoke_dup), MSG_KC_UID_REVOKE_OLD (R.string.msg_kc_uid_revoke_old), + + // keyring consolidation + MSG_KO (R.string.msg_ko), + MSG_KO_FATAL_ENCODE (R.string.msg_ko_fatal_encode), + MSG_KO_HETEROGENEOUS (R.string.msg_ko_heterogeneous), + MSG_KO_MERGING (R.string.msg_ko_merging), + MSG_KO_NEW_SUBKEY (R.string.msg_ko_new_subkey), + MSG_KO_FOUND_NEW (R.string.msg_ko_found_new), ; private final int mMsgId; @@ -264,6 +274,7 @@ public class OperationResultParcel implements Parcelable { /// Simple convenience method public void add(LogLevel level, LogType type, String[] parameters, int indent) { + Log.d(Constants.TAG, type.toString()); add(new OperationResultParcel.LogEntryParcel(level, type, parameters, indent)); } -- cgit v1.2.3 From 1e45e5cd9ad4e38bcff347bc7d3b8f422a636519 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Wed, 18 Jun 2014 10:00:21 +0200 Subject: code cleanup in ProviderHelper and UncachedKeyRing --- .../keychain/pgp/UncachedKeyRing.java | 46 ++++++++++------------ .../keychain/provider/ProviderHelper.java | 12 +----- 2 files changed, 22 insertions(+), 36 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java index 5cd13c4a2..5b353efbf 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java @@ -51,6 +51,7 @@ import java.util.Vector; * @see org.sufficientlysecure.keychain.pgp.UncachedSecretKey * */ +@SuppressWarnings("unchecked") public class UncachedKeyRing { final PGPKeyRing mRing; @@ -65,12 +66,6 @@ public class UncachedKeyRing { return mRing.getPublicKey().getKeyID(); } - /* TODO don't use this */ - @Deprecated - public PGPKeyRing getRing() { - return mRing; - } - public UncachedPublicKey getPublicKey() { return new UncachedPublicKey(mRing.getPublicKey()); } @@ -103,15 +98,6 @@ public class UncachedKeyRing { return mRing.getPublicKey().getFingerprint(); } - public static UncachedKeyRing decodePublicFromData(byte[] data) - throws PgpGeneralException, IOException { - UncachedKeyRing ring = decodeFromData(data); - if(ring.isSecret()) { - throw new PgpGeneralException("Object not recognized as PGPPublicKeyRing!"); - } - return ring; - } - public static UncachedKeyRing decodeFromData(byte[] data) throws PgpGeneralException, IOException { BufferedInputStream bufferedInput = @@ -202,7 +188,8 @@ public class UncachedKeyRing { * @return A canonicalized key, or null on fatal error * */ - public UncachedKeyRing canonicalizePublic(OperationLog log, int indent) { + @SuppressWarnings("ConstantConditions") + public UncachedKeyRing canonicalize(OperationLog log, int indent) { if (isSecret()) { throw new RuntimeException("Tried to public-canonicalize non-public keyring. " + "This is a programming error and should never happen!"); @@ -624,16 +611,6 @@ public class UncachedKeyRing { return new UncachedKeyRing(ring); } - private static PGPKeyRing replacePublicKey(PGPKeyRing ring, PGPPublicKey key) { - if (ring instanceof PGPPublicKeyRing) { - return PGPPublicKeyRing.insertPublicKey((PGPPublicKeyRing) ring, key); - } - PGPSecretKeyRing secRing = (PGPSecretKeyRing) ring; - PGPSecretKey sKey = secRing.getSecretKey(key.getKeyID()); - sKey = PGPSecretKey.replacePublicKey(sKey, key); - return PGPSecretKeyRing.insertSecretKey(secRing, sKey); - } - /** This operation consolidates a list of UncachedKeyRings into a single, combined * UncachedKeyRing. * @@ -759,4 +736,21 @@ public class UncachedKeyRing { } + /** This method replaces a public key in a keyring. + * + * This method essentially wraps PGP*KeyRing.insertPublicKey, where the keyring may be of either + * the secret or public subclass. + * + * @return the resulting PGPKeyRing of the same type as the input + */ + private static PGPKeyRing replacePublicKey(PGPKeyRing ring, PGPPublicKey key) { + if (ring instanceof PGPPublicKeyRing) { + return PGPPublicKeyRing.insertPublicKey((PGPPublicKeyRing) ring, key); + } + PGPSecretKeyRing secRing = (PGPSecretKeyRing) ring; + PGPSecretKey sKey = secRing.getSecretKey(key.getKeyID()); + sKey = PGPSecretKey.replacePublicKey(sKey, key); + return PGPSecretKeyRing.insertSecretKey(secRing, sKey); + } + } 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 cbdf6a64b..248d15ba6 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -68,7 +68,7 @@ import java.util.Set; * name, it is not only a helper but actually the main interface for all * synchronous database operations. * - * Operations in this class write logs (TODO). These can be obtained from the + * Operations in this class write logs. These can be obtained from the * OperationResultParcel return values directly, but are also accumulated over * the lifetime of the executing ProviderHelper object unless the resetLog() * method is called to start a new one specifically. @@ -284,16 +284,8 @@ public class ProviderHelper { new String[]{ PgpKeyHelper.convertKeyIdToHex(masterKeyId) }); mIndent += 1; - try { - WrappedPublicKeyRing ring = getWrappedPublicKeyRing(KeyRings.buildUnifiedKeyRingUri(masterKeyId)); - // ring.get - - } catch(NotFoundException e) { - // no biggie - } - // Canonicalize this key, to assert a number of assumptions made about it. - keyRing = keyRing.canonicalizePublic(mLog, mIndent); + keyRing = keyRing.canonicalize(mLog, mIndent); if (keyRing == null) { return SaveKeyringResult.RESULT_ERROR; } -- cgit v1.2.3 From f80228a08dbb6c9cfa350f9f9f71d76ff8f313c2 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Wed, 18 Jun 2014 13:43:18 +0200 Subject: consolidate: make it work --- .../keychain/pgp/UncachedKeyRing.java | 192 +++++++++++---------- .../keychain/provider/ProviderHelper.java | 13 ++ .../keychain/service/OperationResultParcel.java | 12 +- 3 files changed, 118 insertions(+), 99 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java index 5b353efbf..c06255830 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java @@ -27,11 +27,13 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.util.Arrays; +import java.util.Comparator; import java.util.Date; import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Set; +import java.util.TreeSet; import java.util.Vector; /** Wrapper around PGPKeyRing class, to be constructed from bytes. @@ -190,10 +192,6 @@ public class UncachedKeyRing { */ @SuppressWarnings("ConstantConditions") public UncachedKeyRing canonicalize(OperationLog log, int indent) { - if (isSecret()) { - throw new RuntimeException("Tried to public-canonicalize non-public keyring. " + - "This is a programming error and should never happen!"); - } log.add(LogLevel.START, isSecret() ? LogType.MSG_KC_SECRET : LogType.MSG_KC_PUBLIC, new String[]{PgpKeyHelper.convertKeyIdToHex(getMasterKeyId())}, indent); @@ -611,126 +609,130 @@ public class UncachedKeyRing { return new UncachedKeyRing(ring); } - /** This operation consolidates a list of UncachedKeyRings into a single, combined + /** This operation merges information from a different keyring, returning a combined * UncachedKeyRing. * - * The combined keyring contains the subkeys and user ids of all input keyrings. Even if all - * input keyrings were canonicalized at some point, the resulting keyring will not necessarily - * have that property. - * - * TODO work with secret keys + * The combined keyring contains the subkeys and user ids of both input keyrings, but it does + * not necessarily have the canonicalized property. * - * @param list The list of UncachedKeyRings. Must not be empty, and all of the same masterKeyId - * @return A consolidated UncachedKeyRing with the data of all input keyrings. + * @param other The UncachedKeyRing to merge. Must not be empty, and of the same masterKeyId + * @return A consolidated UncachedKeyRing with the data of both input keyrings. Same type as + * this object, or null on error. * */ - public static UncachedKeyRing consolidate(List list, - OperationLog log, int indent) { + public UncachedKeyRing merge(UncachedKeyRing other, OperationLog log, int indent) { - long masterKeyId = list.get(0).getMasterKeyId(); - - log.add(LogLevel.START, LogType.MSG_KO, - new String[]{ - Integer.toString(list.size()), - PgpKeyHelper.convertKeyIdToHex(masterKeyId) - }, indent); + log.add(LogLevel.START, isSecret() ? LogType.MSG_MG_SECRET : LogType.MSG_MG_PUBLIC, + new String[]{ PgpKeyHelper.convertKeyIdToHex(getMasterKeyId()) }, indent); indent += 1; - // remember which certs we already added - HashSet certs = new HashSet(); + long masterKeyId = other.getMasterKeyId(); - try { - PGPPublicKeyRing result = null; - int num = 1; - for (UncachedKeyRing uring : new IterableIterator(list.iterator())) { - - PGPPublicKeyRing ring = (PGPPublicKeyRing) uring.mRing; - if (uring.getMasterKeyId() != masterKeyId) { - log.add(LogLevel.ERROR, LogType.MSG_KO_HETEROGENEOUS, null, indent); - return null; + if (getMasterKeyId() != masterKeyId) { + log.add(LogLevel.ERROR, LogType.MSG_MG_HETEROGENEOUS, null, indent); + return null; + } + + // remember which certs we already added. this is cheaper than semantic deduplication + Set certs = new TreeSet(new Comparator() { + public int compare(byte[] left, byte[] right) { + // check for length equality + if (left.length != right.length) { + return left.length - right.length; + } + // compare byte-by-byte + for (int i = 0; i < left.length && i < right.length; i++) { + if (left[i] != right[i]) { + return (left[i] & 0xff) - (right[i] & 0xff); + } } + // ok they're the same + return 0; + }}); - // If this is the first ring, just take it - if (result == null) { - result = ring; - continue; + try { + PGPKeyRing result = mRing; + PGPKeyRing candidate = other.mRing; + + // Pre-load all existing certificates + for (PGPPublicKey key : new IterableIterator(result.getPublicKeys())) { + for (PGPSignature cert : new IterableIterator(key.getSignatures())) { + certs.add(cert.getEncoded()); } + } - log.add(LogLevel.DEBUG, LogType.MSG_KO_MERGING, - new String[] { Integer.toString(num++) }, indent); - indent += 1; + // keep track of the number of new certs we add + int newCerts = 0; - // keep track of the number of new certs we add - int newCerts = 0; + for (PGPPublicKey key : new IterableIterator(candidate.getPublicKeys())) { - for (PGPPublicKey key : new IterableIterator(ring.getPublicKeys())) { + final PGPPublicKey resultKey = result.getPublicKey(key.getKeyID()); + if (resultKey == null) { + log.add(LogLevel.DEBUG, LogType.MSG_MG_NEW_SUBKEY, null, indent); + result = replacePublicKey(result, key); + continue; + } - final PGPPublicKey resultkey = result.getPublicKey(key.getKeyID()); - if (resultkey == null) { - log.add(LogLevel.DEBUG, LogType.MSG_KO_NEW_SUBKEY, null, indent); - result = PGPPublicKeyRing.insertPublicKey(result, key); + // Modifiable version of the old key, which we merge stuff into (keep old for comparison) + PGPPublicKey modified = resultKey; + + // Iterate certifications + for (PGPSignature cert : new IterableIterator(key.getSignatures())) { + int type = cert.getSignatureType(); + // Disregard certifications on user ids, we will deal with those later + if (type == PGPSignature.NO_CERTIFICATION + || type == PGPSignature.DEFAULT_CERTIFICATION + || type == PGPSignature.CASUAL_CERTIFICATION + || type == PGPSignature.POSITIVE_CERTIFICATION + || type == PGPSignature.CERTIFICATION_REVOCATION) { continue; } - // The key old key, which we merge stuff into - PGPPublicKey modified = resultkey; - for (PGPSignature cert : new IterableIterator(key.getSignatures())) { - int type = cert.getSignatureType(); - // Disregard certifications on user ids, we will deal with those later - if (type == PGPSignature.NO_CERTIFICATION - || type == PGPSignature.DEFAULT_CERTIFICATION - || type == PGPSignature.CASUAL_CERTIFICATION - || type == PGPSignature.POSITIVE_CERTIFICATION - || type == PGPSignature.CERTIFICATION_REVOCATION) { - continue; - } - - int hash = Arrays.hashCode(cert.getEncoded()); - // Known cert, skip it - if (certs.contains(hash)) { - continue; - } - certs.add(hash); - modified = PGPPublicKey.addCertification(modified, cert); - newCerts += 1; + byte[] encoded = cert.getEncoded(); + // Known cert, skip it + if (certs.contains(encoded)) { + continue; } + certs.add(encoded); + modified = PGPPublicKey.addCertification(modified, cert); + newCerts += 1; + } - // If this is a subkey, stop here - if (!key.isMasterKey()) { - result = PGPPublicKeyRing.insertPublicKey(result, modified); - continue; + // If this is a subkey, merge it in and stop here + if (!key.isMasterKey()) { + if (modified != resultKey) { + result = replacePublicKey(result, modified); } + continue; + } - // Copy over all user id certificates - for (String userId : new IterableIterator(key.getUserIDs())) { - for (PGPSignature cert : new IterableIterator(key.getSignaturesForID(userId))) { - int hash = Arrays.hashCode(cert.getEncoded()); - // Known cert, skip it - if (certs.contains(hash)) { - continue; - } - newCerts += 1; - certs.add(hash); - modified = PGPPublicKey.addCertification(modified, userId, cert); + // Copy over all user id certificates + for (String userId : new IterableIterator(key.getUserIDs())) { + for (PGPSignature cert : new IterableIterator(key.getSignaturesForID(userId))) { + byte[] encoded = cert.getEncoded(); + // Known cert, skip it + if (certs.contains(encoded)) { + continue; } + newCerts += 1; + certs.add(encoded); + modified = PGPPublicKey.addCertification(modified, userId, cert); } - // If anything changed, save the updated (sub)key - if (modified != resultkey) { - result = PGPPublicKeyRing.insertPublicKey(result, modified); - } - } - - log.add(LogLevel.DEBUG, LogType.MSG_KO_FOUND_NEW, - new String[] { Integer.toString(newCerts) }, indent); - + // If anything changed, save the updated (sub)key + if (modified != resultKey) { + result = replacePublicKey(result, modified); + } } + log.add(LogLevel.DEBUG, LogType.MSG_MG_FOUND_NEW, + new String[] { Integer.toString(newCerts) }, indent); + return new UncachedKeyRing(result); } catch (IOException e) { - log.add(LogLevel.ERROR, LogType.MSG_KO_FATAL_ENCODE, null, indent); + log.add(LogLevel.ERROR, LogType.MSG_MG_FATAL_ENCODE, null, indent); return null; } @@ -749,6 +751,10 @@ public class UncachedKeyRing { } PGPSecretKeyRing secRing = (PGPSecretKeyRing) ring; PGPSecretKey sKey = secRing.getSecretKey(key.getKeyID()); + // TODO generate secret key with S2K dummy, if none exists! for now, just die. + if (sKey == null) { + throw new RuntimeException("dummy secret key generation not yet implemented"); + } sKey = PGPSecretKey.replacePublicKey(sKey, key); return PGPSecretKeyRing.insertSecretKey(secRing, sKey); } 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 248d15ba6..457ef1ec8 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -677,6 +677,19 @@ public class ProviderHelper { secretRing = getWrappedSecretKeyRing(keyRing.getMasterKeyId()).getUncached(); log(LogLevel.DEBUG, LogType.MSG_IP_PRESERVING_SECRET); progress.setProgress(LogType.MSG_IP_PRESERVING_SECRET.getMsgId(), 10, 100); + mIndent += 1; + + // Merge data from new public ring into secret one + secretRing = secretRing.merge(keyRing, mLog, mIndent); + if (secretRing == null) { + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + } + + // NOTE that the info from this secret keyring will implicitly be merged into the + // new public keyring, since that one is merged with the old public keyring. + + mIndent -= 1; + } catch (NotFoundException e) { secretRing = null; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index 408715fdf..4010ab108 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -222,12 +222,12 @@ public class OperationResultParcel implements Parcelable { MSG_KC_UID_REVOKE_OLD (R.string.msg_kc_uid_revoke_old), // keyring consolidation - MSG_KO (R.string.msg_ko), - MSG_KO_FATAL_ENCODE (R.string.msg_ko_fatal_encode), - MSG_KO_HETEROGENEOUS (R.string.msg_ko_heterogeneous), - MSG_KO_MERGING (R.string.msg_ko_merging), - MSG_KO_NEW_SUBKEY (R.string.msg_ko_new_subkey), - MSG_KO_FOUND_NEW (R.string.msg_ko_found_new), + MSG_MG_PUBLIC (R.string.msg_mg_public), + MSG_MG_SECRET (R.string.msg_mg_secret), + MSG_MG_FATAL_ENCODE (R.string.msg_mg_fatal_encode), + MSG_MG_HETEROGENEOUS (R.string.msg_mg_heterogeneous), + MSG_MG_NEW_SUBKEY (R.string.msg_mg_new_subkey), + MSG_MG_FOUND_NEW (R.string.msg_mg_found_new), ; private final int mMsgId; -- cgit v1.2.3 From 0bcf7a39bf44994dfcc8e4f8cc52084976b597b0 Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Wed, 18 Jun 2014 16:05:01 +0200 Subject: Continue contact db sync - Only add keyrings to contact db that are not expired nor revoked - Merge all user ids of a key into one contact (#659) - Update contacts: Changes in keyrings (user id add, user id revoke, change of primary id) will be updated into contact db TODO: - delete contact once keyring is removed from OK - sync: wait for key downloads to complete before changing contact db --- .../keychain/helper/ContactHelper.java | 128 ++++++++++++++------- 1 file changed, 89 insertions(+), 39 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ContactHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ContactHelper.java index f50ccf6f8..8e4505004 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ContactHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ContactHelper.java @@ -22,7 +22,6 @@ import android.accounts.AccountManager; import android.content.*; import android.database.Cursor; import android.net.Uri; -import android.os.RemoteException; import android.provider.ContactsContract; import android.util.Patterns; import org.sufficientlysecure.keychain.Constants; @@ -32,10 +31,7 @@ import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.util.Log; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.util.*; public class ContactHelper { @@ -43,12 +39,17 @@ public class ContactHelper { KeychainContract.KeyRings.USER_ID, KeychainContract.KeyRings.FINGERPRINT, KeychainContract.KeyRings.KEY_ID, - KeychainContract.KeyRings.MASTER_KEY_ID}; + KeychainContract.KeyRings.MASTER_KEY_ID, + KeychainContract.KeyRings.EXPIRY, + KeychainContract.KeyRings.IS_REVOKED}; + public static final String[] USER_IDS_PROJECTION = new String[]{ + KeychainContract.UserIds.USER_ID + }; public static final String[] RAW_CONTACT_ID_PROJECTION = new String[]{ContactsContract.RawContacts._ID}; public static final String FIND_RAW_CONTACT_SELECTION = ContactsContract.RawContacts.ACCOUNT_TYPE + "=? AND " + ContactsContract.RawContacts.SOURCE_ID + "=?"; - public static final List getMailAccounts(Context context) { + public static List getMailAccounts(Context context) { final Account[] accounts = AccountManager.get(context).getAccounts(); final Set emailSet = new HashSet(); for (Account account : accounts) { @@ -88,16 +89,24 @@ public class ContactHelper { return null; } + private static ContentProviderOperation.Builder referenceRawContact(ContentProviderOperation.Builder builder, int rawContactId) { + return rawContactId == -1 ? + builder.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) : + builder.withValue(ContactsContract.Data.RAW_CONTACT_ID, rawContactId); + } + public static void writeKeysToContacts(Context context) { ContentResolver resolver = context.getContentResolver(); Cursor cursor = resolver.query(KeychainContract.KeyRings.buildUnifiedKeyRingsUri(), KEYS_TO_CONTACT_PROJECTION, null, null, null); if (cursor != null) { while (cursor.moveToNext()) { - String[] userId = KeyRing.splitUserId(cursor.getString(0)); + String[] primaryUserId = KeyRing.splitUserId(cursor.getString(0)); String fingerprint = PgpKeyHelper.convertFingerprintToHex(cursor.getBlob(1)); String keyIdShort = PgpKeyHelper.convertKeyIdToHexShort(cursor.getLong(2)); long masterKeyId = cursor.getLong(3); + boolean isExpired = !cursor.isNull(4) && new Date(cursor.getLong(4) * 1000).before(new Date()); + boolean isRevoked = cursor.getInt(5) > 0; int rawContactId = -1; Cursor raw = resolver.query(ContactsContract.RawContacts.CONTENT_URI, RAW_CONTACT_ID_PROJECTION, FIND_RAW_CONTACT_SELECTION, new String[]{Constants.PACKAGE_NAME, fingerprint}, null, null); @@ -108,42 +117,83 @@ public class ContactHelper { raw.close(); } ArrayList ops = new ArrayList(); - if (rawContactId == -1) { - ops.add(ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI) - .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, context.getString(R.string.app_name)) - .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, Constants.PACKAGE_NAME) - .withValue(ContactsContract.RawContacts.SOURCE_ID, fingerprint) - .build()); - if (userId[0] != null) { - ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) - .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) - .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) - .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, userId[0]) - .build()); + if (isExpired || isRevoked) { + if (rawContactId != -1) { + resolver.delete(ContactsContract.RawContacts.CONTENT_URI, ContactsContract.RawContacts._ID + "=?", new String[]{Integer.toString(rawContactId)}); } - if (userId[1] != null) { - ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) - .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) - .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE) - .withValue(ContactsContract.CommonDataKinds.Email.DATA, userId[1]) - .build()); + } else { + if (rawContactId == -1) { + insertContact(ops, context, fingerprint); + writeContactKey(ops, context, rawContactId, masterKeyId, keyIdShort); + } + writeContactDisplayName(ops, rawContactId, primaryUserId[0]); + writeContactEmail(ops, resolver, rawContactId, masterKeyId); + try { + resolver.applyBatch(ContactsContract.AUTHORITY, ops); + } catch (Exception e) { + Log.w(Constants.TAG, e); } - ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) - .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) - .withValue(ContactsContract.Data.MIMETYPE, Constants.CUSTOM_CONTACT_DATA_MIME_TYPE) - .withValue(ContactsContract.Data.DATA1, String.format(context.getString(R.string.contact_show_key), keyIdShort)) - .withValue(ContactsContract.Data.DATA2, masterKeyId) - .build()); - } - try { - resolver.applyBatch(ContactsContract.AUTHORITY, ops); - } catch (RemoteException e) { - e.printStackTrace(); - } catch (OperationApplicationException e) { - e.printStackTrace(); } } cursor.close(); } } + + private static void insertContact(ArrayList ops, Context context, String fingerprint) { + ops.add(ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI) + .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, context.getString(R.string.app_name)) + .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, Constants.PACKAGE_NAME) + .withValue(ContactsContract.RawContacts.SOURCE_ID, fingerprint) + .build()); + } + + private static void writeContactKey(ArrayList ops, Context context, int 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) + .withValue(ContactsContract.Data.DATA1, context.getString(R.string.contact_show_key, keyIdShort)) + .withValue(ContactsContract.Data.DATA2, masterKeyId) + .build()); + } + + private static void writeContactEmail(ArrayList ops, ContentResolver resolver, int rawContactId, long masterKeyId) { + ops.add(selectByRawContactAndItemType(ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI), + rawContactId, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE).build()); + Cursor ids = resolver.query(KeychainContract.UserIds.buildUserIdsUri(Long.toString(masterKeyId)), USER_IDS_PROJECTION, KeychainContract.UserIds.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), rawContactId) + .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE) + .withValue(ContactsContract.CommonDataKinds.Email.DATA, userId[1]) + .build()); + } + } + ids.close(); + } + } + + private static void writeContactDisplayName(ArrayList ops, int rawContactId, String displayName) { + if (displayName != null) { + ops.add(insertOrUpdateForRawContact(ContactsContract.Data.CONTENT_URI, rawContactId, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) + .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, displayName) + .build()); + } + } + + private static ContentProviderOperation.Builder insertOrUpdateForRawContact(Uri uri, int rawContactId, String itemType) { + if (rawContactId == -1) { + return referenceRawContact(ContentProviderOperation.newInsert(uri), rawContactId).withValue(ContactsContract.Data.MIMETYPE, itemType); + } else { + return ContentProviderOperation.newUpdate(uri).withSelection( + ContactsContract.Data.RAW_CONTACT_ID + "=? AND " + ContactsContract.Data.MIMETYPE + "=?", + new String[]{Integer.toString(rawContactId), itemType}); + } + } + + private static ContentProviderOperation.Builder selectByRawContactAndItemType(ContentProviderOperation.Builder builder, int rawContactId, String itemType) { + return builder.withSelection( + ContactsContract.Data.RAW_CONTACT_ID + "=? AND " + ContactsContract.Data.MIMETYPE + "=?", + new String[]{Integer.toString(rawContactId), itemType}); + } } -- cgit v1.2.3 From 39a68c30f8d7059080bce06e86de9565a545ee96 Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Wed, 18 Jun 2014 16:08:35 +0200 Subject: Code style --- .../keychain/helper/ContactHelper.java | 39 ++++++++++++++-------- 1 file changed, 26 insertions(+), 13 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ContactHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ContactHelper.java index 8e4505004..66d579e92 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ContactHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ContactHelper.java @@ -79,7 +79,8 @@ public class ContactHelper { } public static Uri dataUriFromContactUri(Context context, Uri contactUri) { - Cursor contactMasterKey = context.getContentResolver().query(contactUri, new String[]{ContactsContract.Data.DATA2}, null, null, null, null); + Cursor contactMasterKey = context.getContentResolver().query(contactUri, + new String[]{ContactsContract.Data.DATA2}, null, null, null, null); if (contactMasterKey != null) { if (contactMasterKey.moveToNext()) { return KeychainContract.KeyRings.buildGenericKeyRingUri(contactMasterKey.getLong(0)); @@ -89,7 +90,8 @@ public class ContactHelper { return null; } - private static ContentProviderOperation.Builder referenceRawContact(ContentProviderOperation.Builder builder, int rawContactId) { + private static ContentProviderOperation.Builder referenceRawContact(ContentProviderOperation.Builder builder, + int rawContactId) { return rawContactId == -1 ? builder.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) : builder.withValue(ContactsContract.Data.RAW_CONTACT_ID, rawContactId); @@ -119,7 +121,8 @@ public class ContactHelper { ArrayList ops = new ArrayList(); if (isExpired || isRevoked) { if (rawContactId != -1) { - resolver.delete(ContactsContract.RawContacts.CONTENT_URI, ContactsContract.RawContacts._ID + "=?", new String[]{Integer.toString(rawContactId)}); + resolver.delete(ContactsContract.RawContacts.CONTENT_URI, + ContactsContract.RawContacts._ID + "=?", new String[]{Integer.toString(rawContactId)}); } } else { if (rawContactId == -1) { @@ -147,7 +150,8 @@ public class ContactHelper { .build()); } - private static void writeContactKey(ArrayList ops, Context context, int rawContactId, long masterKeyId, String keyIdShort) { + private static void writeContactKey(ArrayList ops, Context context, int 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) .withValue(ContactsContract.Data.DATA1, context.getString(R.string.contact_show_key, keyIdShort)) @@ -155,16 +159,20 @@ public class ContactHelper { .build()); } - private static void writeContactEmail(ArrayList ops, ContentResolver resolver, int rawContactId, long masterKeyId) { + private static void writeContactEmail(ArrayList ops, ContentResolver resolver, + int rawContactId, long masterKeyId) { ops.add(selectByRawContactAndItemType(ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI), rawContactId, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE).build()); - Cursor ids = resolver.query(KeychainContract.UserIds.buildUserIdsUri(Long.toString(masterKeyId)), USER_IDS_PROJECTION, KeychainContract.UserIds.IS_REVOKED + "=0", null, null); + Cursor ids = resolver.query(KeychainContract.UserIds.buildUserIdsUri(Long.toString(masterKeyId)), + USER_IDS_PROJECTION, KeychainContract.UserIds.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), rawContactId) - .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE) + ops.add(referenceRawContact(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI), + rawContactId) + .withValue(ContactsContract.Data.MIMETYPE, + ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE) .withValue(ContactsContract.CommonDataKinds.Email.DATA, userId[1]) .build()); } @@ -173,17 +181,21 @@ public class ContactHelper { } } - private static void writeContactDisplayName(ArrayList ops, int rawContactId, String displayName) { + private static void writeContactDisplayName(ArrayList ops, int rawContactId, + String displayName) { if (displayName != null) { - ops.add(insertOrUpdateForRawContact(ContactsContract.Data.CONTENT_URI, rawContactId, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) + ops.add(insertOrUpdateForRawContact(ContactsContract.Data.CONTENT_URI, rawContactId, + ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, displayName) .build()); } } - private static ContentProviderOperation.Builder insertOrUpdateForRawContact(Uri uri, int rawContactId, String itemType) { + private static ContentProviderOperation.Builder insertOrUpdateForRawContact(Uri uri, int rawContactId, + String itemType) { if (rawContactId == -1) { - return referenceRawContact(ContentProviderOperation.newInsert(uri), rawContactId).withValue(ContactsContract.Data.MIMETYPE, itemType); + return referenceRawContact(ContentProviderOperation.newInsert(uri), rawContactId).withValue( + ContactsContract.Data.MIMETYPE, itemType); } else { return ContentProviderOperation.newUpdate(uri).withSelection( ContactsContract.Data.RAW_CONTACT_ID + "=? AND " + ContactsContract.Data.MIMETYPE + "=?", @@ -191,7 +203,8 @@ public class ContactHelper { } } - private static ContentProviderOperation.Builder selectByRawContactAndItemType(ContentProviderOperation.Builder builder, int rawContactId, String itemType) { + private static ContentProviderOperation.Builder selectByRawContactAndItemType( + ContentProviderOperation.Builder builder, int rawContactId, String itemType) { return builder.withSelection( ContactsContract.Data.RAW_CONTACT_ID + "=? AND " + ContactsContract.Data.MIMETYPE + "=?", new String[]{Integer.toString(rawContactId), itemType}); -- cgit v1.2.3 From 4bbaf6faa1b29bd66f662eb34208bef91260987b Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Wed, 18 Jun 2014 16:47:33 +0200 Subject: Remove abandoned contacts --- .../keychain/helper/ContactHelper.java | 116 ++++++++++++++++----- 1 file changed, 90 insertions(+), 26 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ContactHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ContactHelper.java index 66d579e92..d8a7e8427 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ContactHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ContactHelper.java @@ -45,9 +45,18 @@ public class ContactHelper { public static final String[] USER_IDS_PROJECTION = new String[]{ KeychainContract.UserIds.USER_ID }; - public static final String[] RAW_CONTACT_ID_PROJECTION = new String[]{ContactsContract.RawContacts._ID}; - public static final String FIND_RAW_CONTACT_SELECTION = + + public static final String NON_REVOKED_SELECTION = KeychainContract.UserIds.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 + "=?"; public static List getMailAccounts(Context context) { final Account[] accounts = AccountManager.get(context).getAccounts(); @@ -90,45 +99,44 @@ public class ContactHelper { return null; } - private static ContentProviderOperation.Builder referenceRawContact(ContentProviderOperation.Builder builder, - int rawContactId) { - return rawContactId == -1 ? - builder.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) : - builder.withValue(ContactsContract.Data.RAW_CONTACT_ID, rawContactId); - } - + /** + * Write the current Keychain to the contact db + */ public static void writeKeysToContacts(Context context) { ContentResolver resolver = context.getContentResolver(); + Set contactFingerprints = getRawContactFingerprints(resolver); + + // Load all Keys from OK Cursor cursor = resolver.query(KeychainContract.KeyRings.buildUnifiedKeyRingsUri(), KEYS_TO_CONTACT_PROJECTION, null, null, null); if (cursor != null) { while (cursor.moveToNext()) { String[] primaryUserId = KeyRing.splitUserId(cursor.getString(0)); String fingerprint = PgpKeyHelper.convertFingerprintToHex(cursor.getBlob(1)); + contactFingerprints.remove(fingerprint); String keyIdShort = PgpKeyHelper.convertKeyIdToHexShort(cursor.getLong(2)); long masterKeyId = cursor.getLong(3); boolean isExpired = !cursor.isNull(4) && new Date(cursor.getLong(4) * 1000).before(new Date()); boolean isRevoked = cursor.getInt(5) > 0; - int rawContactId = -1; - Cursor raw = resolver.query(ContactsContract.RawContacts.CONTENT_URI, RAW_CONTACT_ID_PROJECTION, - FIND_RAW_CONTACT_SELECTION, new String[]{Constants.PACKAGE_NAME, fingerprint}, null, null); - if (raw != null) { - if (raw.moveToNext()) { - rawContactId = raw.getInt(0); - } - raw.close(); - } + int rawContactId = findRawContactId(resolver, fingerprint); ArrayList ops = new ArrayList(); + + // Do not store expired or revoked keys in contact db - and remove them if they already exist if (isExpired || isRevoked) { if (rawContactId != -1) { - resolver.delete(ContactsContract.RawContacts.CONTENT_URI, - ContactsContract.RawContacts._ID + "=?", new String[]{Integer.toString(rawContactId)}); + resolver.delete(ContactsContract.RawContacts.CONTENT_URI, ID_SELECTION, + new String[]{Integer.toString(rawContactId)}); } } else { + + // Create a new rawcontact with corresponding key if it does not exist yet if (rawContactId == -1) { insertContact(ops, context, fingerprint); 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]); writeContactEmail(ops, resolver, rawContactId, masterKeyId); try { @@ -140,8 +148,52 @@ public class ContactHelper { } cursor.close(); } + + // Delete fingerprints that are no longer present in OK + for (String fingerprint : contactFingerprints) { + resolver.delete(ContactsContract.RawContacts.CONTENT_URI, ACCOUNT_TYPE_AND_SOURCE_ID_SELECTION, + new String[]{Constants.PACKAGE_NAME, fingerprint}); + } + } + /** + * @return a set of all key fingerprints currently present in the contact db + */ + private static Set getRawContactFingerprints(ContentResolver resolver) { + HashSet result = new HashSet(); + Cursor fingerprints = resolver.query(ContactsContract.RawContacts.CONTENT_URI, SOURCE_ID_PROJECTION, + ACCOUNT_TYPE_SELECTION, new String[]{Constants.PACKAGE_NAME}, null); + if (fingerprints != null) { + while (fingerprints.moveToNext()) { + result.add(fingerprints.getString(0)); + } + fingerprints.close(); + } + return result; + } + + /** + * This will search the contact db for a raw contact with a given fingerprint + * + * @return raw contact id or -1 if not found + */ + 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.PACKAGE_NAME, fingerprint}, null, null); + if (raw != null) { + if (raw.moveToNext()) { + rawContactId = raw.getInt(0); + } + raw.close(); + } + return rawContactId; + } + + /** + * Creates a empty raw contact with a given fingerprint + */ private static void insertContact(ArrayList ops, Context context, String fingerprint) { ops.add(ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI) .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, context.getString(R.string.app_name)) @@ -150,6 +202,11 @@ public class ContactHelper { .build()); } + /** + * Adds a key id to the given raw contact. + *

+ * This creates the link to OK in contact details + */ private static void writeContactKey(ArrayList ops, Context context, int rawContactId, long masterKeyId, String keyIdShort) { ops.add(referenceRawContact(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI), rawContactId) @@ -159,12 +216,15 @@ public class ContactHelper { .build()); } + /** + * Write all known email addresses of a key (derived from user ids) to a given raw contact + */ private static void writeContactEmail(ArrayList ops, ContentResolver resolver, int rawContactId, long masterKeyId) { ops.add(selectByRawContactAndItemType(ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI), rawContactId, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE).build()); Cursor ids = resolver.query(KeychainContract.UserIds.buildUserIdsUri(Long.toString(masterKeyId)), - USER_IDS_PROJECTION, KeychainContract.UserIds.IS_REVOKED + "=0", null, null); + USER_IDS_PROJECTION, NON_REVOKED_SELECTION, null, null); if (ids != null) { while (ids.moveToNext()) { String[] userId = KeyRing.splitUserId(ids.getString(0)); @@ -191,22 +251,26 @@ public class ContactHelper { } } + private static ContentProviderOperation.Builder referenceRawContact(ContentProviderOperation.Builder builder, + int 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, String itemType) { if (rawContactId == -1) { return referenceRawContact(ContentProviderOperation.newInsert(uri), rawContactId).withValue( ContactsContract.Data.MIMETYPE, itemType); } else { - return ContentProviderOperation.newUpdate(uri).withSelection( - ContactsContract.Data.RAW_CONTACT_ID + "=? AND " + ContactsContract.Data.MIMETYPE + "=?", - new String[]{Integer.toString(rawContactId), itemType}); + return selectByRawContactAndItemType(ContentProviderOperation.newUpdate(uri), rawContactId, itemType); } } private static ContentProviderOperation.Builder selectByRawContactAndItemType( ContentProviderOperation.Builder builder, int rawContactId, String itemType) { - return builder.withSelection( - ContactsContract.Data.RAW_CONTACT_ID + "=? AND " + ContactsContract.Data.MIMETYPE + "=?", + return builder.withSelection(RAW_CONTACT_AND_MIMETYPE_SELECTION, new String[]{Integer.toString(rawContactId), itemType}); } } -- cgit v1.2.3 From b9d88de286022f29561eff3e6826e3052eb9c9ea Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Wed, 18 Jun 2014 17:01:36 +0200 Subject: Wait for import to be done before writing contacts --- .../keychain/service/ContactSyncAdapterService.java | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java index 8db9294df..b245c5e2a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java @@ -30,10 +30,14 @@ import org.sufficientlysecure.keychain.helper.ContactHelper; import org.sufficientlysecure.keychain.helper.EmailKeyHelper; import org.sufficientlysecure.keychain.util.Log; +import java.util.concurrent.atomic.AtomicBoolean; + public class ContactSyncAdapterService extends Service { private class ContactSyncAdapter extends AbstractThreadedSyncAdapter { + private final AtomicBoolean importDone = new AtomicBoolean(false); + public ContactSyncAdapter() { super(ContactSyncAdapterService.this, true); } @@ -41,6 +45,7 @@ public class ContactSyncAdapterService extends Service { @Override public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, final SyncResult syncResult) { + KeychainApplication.setupAccountAsNeeded(ContactSyncAdapterService.this); EmailKeyHelper.importContacts(getContext(), new Messenger(new Handler(Looper.getMainLooper(), new Handler.Callback() { @Override @@ -48,11 +53,16 @@ public class ContactSyncAdapterService extends Service { Bundle data = msg.getData(); switch (msg.arg1) { case KeychainIntentServiceHandler.MESSAGE_OKAY: + Log.d(Constants.TAG, "Syncing... Done."); + synchronized (importDone) { + importDone.set(true); + importDone.notifyAll(); + } return true; case KeychainIntentServiceHandler.MESSAGE_UPDATE_PROGRESS: if (data.containsKey(KeychainIntentServiceHandler.DATA_PROGRESS) && data.containsKey(KeychainIntentServiceHandler.DATA_PROGRESS_MAX)) { - Log.d(Constants.TAG, "Progress: " + + Log.d(Constants.TAG, "Syncing... Progress: " + data.getInt(KeychainIntentServiceHandler.DATA_PROGRESS) + "/" + data.getInt(KeychainIntentServiceHandler.DATA_PROGRESS_MAX)); return false; @@ -63,7 +73,14 @@ public class ContactSyncAdapterService extends Service { } } }))); - KeychainApplication.setupAccountAsNeeded(ContactSyncAdapterService.this); + synchronized (importDone) { + try { + if (!importDone.get()) importDone.wait(); + } catch (InterruptedException e) { + Log.w(Constants.TAG, e); + return; + } + } ContactHelper.writeKeysToContacts(ContactSyncAdapterService.this); } } -- cgit v1.2.3 From a1c3c41073d283dc951e50199d409e28ee4a2ee8 Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Wed, 18 Jun 2014 17:06:46 +0200 Subject: Reset importDone at the beginning of sync --- .../sufficientlysecure/keychain/service/ContactSyncAdapterService.java | 1 + 1 file changed, 1 insertion(+) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java index b245c5e2a..6c4d59a77 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java @@ -45,6 +45,7 @@ public class ContactSyncAdapterService extends Service { @Override public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, final SyncResult syncResult) { + importDone.set(false); KeychainApplication.setupAccountAsNeeded(ContactSyncAdapterService.this); EmailKeyHelper.importContacts(getContext(), new Messenger(new Handler(Looper.getMainLooper(), new Handler.Callback() { -- cgit v1.2.3 From 134f8471c05724c312085ce5b8eae2aec8cb8a52 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Wed, 18 Jun 2014 19:39:16 +0200 Subject: consolidate: add key import routines with consolidation --- .../keychain/pgp/PgpImportExport.java | 3 +- .../keychain/pgp/UncachedKeyRing.java | 23 ++- .../keychain/pgp/WrappedKeyRing.java | 4 + .../keychain/pgp/WrappedSecretKeyRing.java | 4 - .../keychain/provider/ProviderHelper.java | 209 ++++++++++++++++----- .../keychain/service/KeychainIntentService.java | 2 +- 6 files changed, 195 insertions(+), 50 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java index 3681d62d8..9b070175c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java @@ -149,7 +149,8 @@ public class PgpImportExport { SaveKeyringResult result; if (key.isSecret()) { - result = mProviderHelper.saveSecretKeyRing(key); + result = mProviderHelper.saveSecretKeyRing(key, + new ProgressScaler(mProgressable, position, (position+1)*progSteps, 100)); } else { result = mProviderHelper.savePublicKeyRing(key, new ProgressScaler(mProgressable, position, (position+1)*progSteps, 100)); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java index c06255830..0f0d7cca3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java @@ -58,10 +58,18 @@ public class UncachedKeyRing { final PGPKeyRing mRing; final boolean mIsSecret; + final boolean mIsCanonicalized; UncachedKeyRing(PGPKeyRing ring) { mRing = ring; mIsSecret = ring instanceof PGPSecretKeyRing; + mIsCanonicalized = false; + } + + private UncachedKeyRing(PGPKeyRing ring, boolean canonicalized) { + mRing = ring; + mIsSecret = ring instanceof PGPSecretKeyRing; + mIsCanonicalized = canonicalized; } public long getMasterKeyId() { @@ -92,6 +100,10 @@ public class UncachedKeyRing { return mIsSecret; } + public boolean isCanonicalized() { + return mIsCanonicalized; + } + public byte[] getEncoded() throws IOException { return mRing.getEncoded(); } @@ -606,7 +618,7 @@ public class UncachedKeyRing { log.add(LogLevel.OK, LogType.MSG_KC_SUCCESS, null, indent); } - return new UncachedKeyRing(ring); + return new UncachedKeyRing(ring, true); } /** This operation merges information from a different keyring, returning a combined @@ -688,6 +700,11 @@ public class UncachedKeyRing { continue; } + // Don't merge foreign stuff into secret keys + if (cert.getKeyID() != masterKeyId && isSecret()) { + continue; + } + byte[] encoded = cert.getEncoded(); // Known cert, skip it if (certs.contains(encoded)) { @@ -709,6 +726,10 @@ public class UncachedKeyRing { // Copy over all user id certificates for (String userId : new IterableIterator(key.getUserIDs())) { for (PGPSignature cert : new IterableIterator(key.getSignaturesForID(userId))) { + // Don't merge foreign stuff into secret keys + if (cert.getKeyID() != masterKeyId && isSecret()) { + continue; + } byte[] encoded = cert.getEncoded(); // Known cert, skip it if (certs.contains(encoded)) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedKeyRing.java index 632a04fc3..6f3068261 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedKeyRing.java @@ -101,4 +101,8 @@ public abstract class WrappedKeyRing extends KeyRing { abstract public IterableIterator publicKeyIterator(); + public UncachedKeyRing getUncached() { + return new UncachedKeyRing(getRing()); + } + } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKeyRing.java index 9591cf8bc..d7148f710 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKeyRing.java @@ -154,8 +154,4 @@ public class WrappedSecretKeyRing extends WrappedKeyRing { }); } - public UncachedKeyRing getUncached() { - return new UncachedKeyRing(mRing); - } - } 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 457ef1ec8..36b4b4ac7 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -57,6 +57,7 @@ import org.sufficientlysecure.keychain.util.Log; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.HashMap; @@ -275,21 +276,15 @@ public class ProviderHelper { log(LogLevel.ERROR, LogType.MSG_IP_BAD_TYPE_SECRET); return SaveKeyringResult.RESULT_ERROR; } + if (!keyRing.isCanonicalized()) { + log(LogLevel.ERROR, LogType.MSG_IP_BAD_TYPE_SECRET); + return SaveKeyringResult.RESULT_ERROR; + } // start with ok result int result = SaveKeyringResult.SAVED_PUBLIC; long masterKeyId = keyRing.getMasterKeyId(); - log(LogLevel.START, LogType.MSG_IP, - new String[]{ PgpKeyHelper.convertKeyIdToHex(masterKeyId) }); - mIndent += 1; - - // Canonicalize this key, to assert a number of assumptions made about it. - keyRing = keyRing.canonicalize(mLog, mIndent); - if (keyRing == null) { - return SaveKeyringResult.RESULT_ERROR; - } - UncachedPublicKey masterKey = keyRing.getPublicKey(); ArrayList operations; @@ -569,11 +564,16 @@ public class ProviderHelper { /** Saves an UncachedKeyRing of the secret variant into the db. * This method will fail if no corresponding public keyring is in the database! */ - private SaveKeyringResult internalSaveSecretKeyRing(UncachedKeyRing keyRing) { + private int internalSaveSecretKeyRing(UncachedKeyRing keyRing) { if (!keyRing.isSecret()) { log(LogLevel.ERROR, LogType.MSG_IS_BAD_TYPE_PUBLIC); - return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + return SaveKeyringResult.RESULT_ERROR; + } + + if (!keyRing.isCanonicalized()) { + log(LogLevel.ERROR, LogType.MSG_IS_BAD_TYPE_PUBLIC); + return SaveKeyringResult.RESULT_ERROR; } long masterKeyId = keyRing.getMasterKeyId(); @@ -584,7 +584,7 @@ public class ProviderHelper { // Canonicalize this key, to assert a number of assumptions made about it. keyRing = keyRing.canonicalize(mLog, mIndent); if (keyRing == null) { - return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + return SaveKeyringResult.RESULT_ERROR; } // IF this is successful, it's a secret key @@ -599,12 +599,12 @@ public class ProviderHelper { Uri uri = KeyRingData.buildSecretKeyRingUri(Long.toString(masterKeyId)); if (mContentResolver.insert(uri, values) == null) { log(LogLevel.ERROR, LogType.MSG_IS_DB_EXCEPTION); - return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + return SaveKeyringResult.RESULT_ERROR; } } catch (IOException e) { Log.e(Constants.TAG, "Failed to encode key!", e); log(LogLevel.ERROR, LogType.MSG_IS_IO_EXCPTION); - return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + return SaveKeyringResult.RESULT_ERROR; } { @@ -648,11 +648,12 @@ public class ProviderHelper { } log(LogLevel.OK, LogType.MSG_IS_SUCCESS); - return new SaveKeyringResult(result, mLog); + return result; } + @Deprecated public SaveKeyringResult savePublicKeyRing(UncachedKeyRing keyRing) { return savePublicKeyRing(keyRing, new Progressable() { @Override @@ -669,54 +670,176 @@ public class ProviderHelper { }); } - public SaveKeyringResult savePublicKeyRing(UncachedKeyRing keyRing, Progressable progress) { + /** Save a public keyring into the database. + * + * This is a high level method, which takes care of merging all new information into the old and + * keep public and secret keyrings in sync. + */ + public SaveKeyringResult savePublicKeyRing(UncachedKeyRing publicRing, Progressable progress) { - // IF there is a secret key, preserve it! - UncachedKeyRing secretRing; try { - secretRing = getWrappedSecretKeyRing(keyRing.getMasterKeyId()).getUncached(); - log(LogLevel.DEBUG, LogType.MSG_IP_PRESERVING_SECRET); - progress.setProgress(LogType.MSG_IP_PRESERVING_SECRET.getMsgId(), 10, 100); + long masterKeyId = publicRing.getMasterKeyId(); + log(LogLevel.START, LogType.MSG_IP, + new String[]{ PgpKeyHelper.convertKeyIdToHex(masterKeyId) }); + mIndent += 1; - // Merge data from new public ring into secret one - secretRing = secretRing.merge(keyRing, mLog, mIndent); - if (secretRing == null) { + // IF there is a secret key, preserve it! + try { + UncachedKeyRing oldPublicRing = getWrappedPublicKeyRing(masterKeyId).getUncached(); + + // Merge data from new public ring into the old one + publicRing = oldPublicRing.merge(publicRing, mLog, mIndent); + + if (publicRing == null) { + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + } + + // Early breakout if nothing changed + if (Arrays.hashCode(publicRing.getEncoded()) + == Arrays.hashCode(oldPublicRing.getEncoded())) { + log(LogLevel.OK, LogType.MSG_IP, + new String[]{ PgpKeyHelper.convertKeyIdToHex(masterKeyId) }); + return new SaveKeyringResult(SaveKeyringResult.RESULT_OK, mLog); + } + } catch (NotFoundException e) { + // not an issue, just means we are dealing with a new keyring + } + + // Canonicalize this keyring, to assert a number of assumptions made about it. + publicRing = publicRing.canonicalize(mLog, mIndent); + if (publicRing == null) { return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); } - // NOTE that the info from this secret keyring will implicitly be merged into the - // new public keyring, since that one is merged with the old public keyring. + // IF there is a secret key, preserve it! + UncachedKeyRing secretRing; + try { + secretRing = getWrappedSecretKeyRing(publicRing.getMasterKeyId()).getUncached(); + log(LogLevel.DEBUG, LogType.MSG_IP_PRESERVING_SECRET); + progress.setProgress(LogType.MSG_IP_PRESERVING_SECRET.getMsgId(), 10, 100); + mIndent += 1; - mIndent -= 1; + // Merge data from new public ring into secret one + secretRing = secretRing.merge(publicRing, mLog, mIndent); + if (secretRing == null) { + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + } + + mIndent -= 1; + + } catch (NotFoundException e) { + secretRing = null; + } + + int result = internalSavePublicKeyRing(publicRing, progress, secretRing != null); + + // Save the saved keyring (if any) + if (secretRing != null) { + log(LogLevel.DEBUG, LogType.MSG_IP_REINSERT_SECRET); + progress.setProgress(LogType.MSG_IP_REINSERT_SECRET.getMsgId(), 90, 100); + mIndent += 1; + secretRing = secretRing.canonicalize(mLog, mIndent); + internalSaveSecretKeyRing(secretRing); + result |= SaveKeyringResult.SAVED_SECRET; + mIndent -= 1; + } + + return new SaveKeyringResult(result, mLog); - } catch (NotFoundException e) { - secretRing = null; + } catch (IOException e) { + return null; } - int result = internalSavePublicKeyRing(keyRing, progress, secretRing != null); + } + + public SaveKeyringResult saveSecretKeyRing(UncachedKeyRing secretRing, Progressable progress) { + + try { + long masterKeyId = secretRing.getMasterKeyId(); + log(LogLevel.START, LogType.MSG_IS, + new String[]{ PgpKeyHelper.convertKeyIdToHex(masterKeyId) }); - // Save the saved keyring (if any) - if (secretRing != null) { - log(LogLevel.DEBUG, LogType.MSG_IP_REINSERT_SECRET); - progress.setProgress(LogType.MSG_IP_REINSERT_SECRET.getMsgId(), 90, 100); mIndent += 1; - internalSaveSecretKeyRing(secretRing); - result |= SaveKeyringResult.SAVED_SECRET; - mIndent -= 1; - } - return new SaveKeyringResult(result, mLog); + // If there is a secret key, merge it. + try { + UncachedKeyRing oldSecretRing = getWrappedSecretKeyRing(masterKeyId).getUncached(); - } + // Merge data from new public ring into the old one + secretRing = oldSecretRing.merge(secretRing, mLog, mIndent); + + if (secretRing == null) { + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + } + + // Early breakout if nothing changed + if (Arrays.hashCode(secretRing.getEncoded()) + == Arrays.hashCode(oldSecretRing.getEncoded())) { + log(LogLevel.OK, LogType.MSG_IS, + new String[]{ PgpKeyHelper.convertKeyIdToHex(masterKeyId) }); + return new SaveKeyringResult(SaveKeyringResult.RESULT_OK, mLog); + } + } catch (NotFoundException e) { + // not an issue, just means we are dealing with a new keyring + } + + // Canonicalize this keyring, to assert a number of assumptions made about it. + secretRing = secretRing.canonicalize(mLog, mIndent); + if (secretRing == null) { + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + } + + // Merge new data into public keyring as well, if there is any + try { + UncachedKeyRing oldPublicRing = getWrappedPublicKeyRing(masterKeyId).getUncached(); + log(LogLevel.DEBUG, LogType.MSG_IP_PRESERVING_SECRET); + progress.setProgress(LogType.MSG_IP_PRESERVING_SECRET.getMsgId(), 10, 100); + mIndent += 1; + + // Merge data from new public ring into secret one + UncachedKeyRing publicRing = oldPublicRing.merge(secretRing, mLog, mIndent); + if (publicRing == null) { + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + } + + // Early breakout if nothing changed + if (Arrays.hashCode(publicRing.getEncoded()) + != Arrays.hashCode(oldPublicRing.getEncoded())) { + + log(LogLevel.OK, LogType.MSG_IS, + new String[]{ PgpKeyHelper.convertKeyIdToHex(masterKeyId) }); + + publicRing = publicRing.canonicalize(mLog, mIndent); + if (publicRing == null) { + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + } + + int result = internalSavePublicKeyRing(publicRing, progress, true); + + } + + mIndent -= 1; + + } catch (NotFoundException e) { + // TODO, this WILL error out later because secret rings cannot be inserted without + // public ones + } + + progress.setProgress(LogType.MSG_IP_REINSERT_SECRET.getMsgId(), 90, 100); + int result = internalSaveSecretKeyRing(secretRing); + return new SaveKeyringResult(result, mLog); + + } catch (IOException e) { + return null; + } - public SaveKeyringResult saveSecretKeyRing(UncachedKeyRing keyRing) { - return internalSaveSecretKeyRing(keyRing); } /** * Saves (or updates) a pair of public and secret KeyRings in the database */ + @Deprecated // scheduled for deletion after merge with new-edit branch public void savePairedKeyRing(UncachedKeyRing pubRing, UncachedKeyRing secRing) throws IOException { long masterKeyId = pubRing.getMasterKeyId(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java index e30d48ce5..5358f36e8 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -513,7 +513,7 @@ public class KeychainIntentService extends IntentService UncachedKeyRing newKeyRing = keyRing.changeSecretKeyPassphrase(oldPassphrase, newPassphrase); setProgress(R.string.progress_saving_key_ring, 50, 100); - providerHelper.saveSecretKeyRing(newKeyRing); + // providerHelper.saveSecretKeyRing(newKeyRing); setProgress(R.string.progress_done, 100, 100); } else { PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 90, 100)); -- cgit v1.2.3 From f8d895dea4ba1a5133107474410813f4128176ef Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Wed, 18 Jun 2014 20:13:01 +0200 Subject: consolidate: almost sane logging --- .../keychain/pgp/UncachedKeyRing.java | 2 +- .../keychain/provider/ProviderHelper.java | 96 ++++++++++++---------- .../keychain/service/OperationResultParcel.java | 7 +- 3 files changed, 59 insertions(+), 46 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java index 0f0d7cca3..a1c6b158b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java @@ -634,7 +634,7 @@ public class UncachedKeyRing { */ public UncachedKeyRing merge(UncachedKeyRing other, OperationLog log, int indent) { - log.add(LogLevel.START, isSecret() ? LogType.MSG_MG_SECRET : LogType.MSG_MG_PUBLIC, + log.add(LogLevel.DEBUG, isSecret() ? LogType.MSG_MG_SECRET : LogType.MSG_MG_PUBLIC, new String[]{ PgpKeyHelper.convertKeyIdToHex(getMasterKeyId()) }, indent); indent += 1; 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 36b4b4ac7..79bd5777c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -532,7 +532,7 @@ public class ProviderHelper { mIndent -= 1; return SaveKeyringResult.RESULT_ERROR; } catch (OperationApplicationException e) { - log(LogLevel.ERROR, LogType.MSG_IP_FAIL_OP_EX); + log(LogLevel.ERROR, LogType.MSG_IP_FAIL_OP_EXC); Log.e(Constants.TAG, "OperationApplicationException during import", e); mIndent -= 1; return SaveKeyringResult.RESULT_ERROR; @@ -603,7 +603,7 @@ public class ProviderHelper { } } catch (IOException e) { Log.e(Constants.TAG, "Failed to encode key!", e); - log(LogLevel.ERROR, LogType.MSG_IS_IO_EXCPTION); + log(LogLevel.ERROR, LogType.MSG_IS_FAIL_IO_EXC); return SaveKeyringResult.RESULT_ERROR; } @@ -681,16 +681,22 @@ public class ProviderHelper { long masterKeyId = publicRing.getMasterKeyId(); log(LogLevel.START, LogType.MSG_IP, new String[]{ PgpKeyHelper.convertKeyIdToHex(masterKeyId) }); - mIndent += 1; - // IF there is a secret key, preserve it! + // If there is an old keyring, merge it try { UncachedKeyRing oldPublicRing = getWrappedPublicKeyRing(masterKeyId).getUncached(); // Merge data from new public ring into the old one publicRing = oldPublicRing.merge(publicRing, mLog, mIndent); + // If this is null, there is an error in the log so we can just return + if (publicRing == null) { + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + } + + // Canonicalize this keyring, to assert a number of assumptions made about it. + publicRing = publicRing.canonicalize(mLog, mIndent); if (publicRing == null) { return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); } @@ -698,37 +704,37 @@ public class ProviderHelper { // Early breakout if nothing changed if (Arrays.hashCode(publicRing.getEncoded()) == Arrays.hashCode(oldPublicRing.getEncoded())) { - log(LogLevel.OK, LogType.MSG_IP, - new String[]{ PgpKeyHelper.convertKeyIdToHex(masterKeyId) }); + log(LogLevel.OK, LogType.MSG_IP_SUCCESS_IDENTICAL, null); return new SaveKeyringResult(SaveKeyringResult.RESULT_OK, mLog); } } catch (NotFoundException e) { - // not an issue, just means we are dealing with a new keyring - } + // Not an issue, just means we are dealing with a new keyring. + + // Canonicalize this keyring, to assert a number of assumptions made about it. + publicRing = publicRing.canonicalize(mLog, mIndent); + if (publicRing == null) { + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + } - // Canonicalize this keyring, to assert a number of assumptions made about it. - publicRing = publicRing.canonicalize(mLog, mIndent); - if (publicRing == null) { - return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); } - // IF there is a secret key, preserve it! + // If there is a secret key, merge new data (if any) and save the key for later UncachedKeyRing secretRing; try { secretRing = getWrappedSecretKeyRing(publicRing.getMasterKeyId()).getUncached(); - log(LogLevel.DEBUG, LogType.MSG_IP_PRESERVING_SECRET); - progress.setProgress(LogType.MSG_IP_PRESERVING_SECRET.getMsgId(), 10, 100); - mIndent += 1; // Merge data from new public ring into secret one secretRing = secretRing.merge(publicRing, mLog, mIndent); if (secretRing == null) { return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); } - - mIndent -= 1; + secretRing = secretRing.canonicalize(mLog, mIndent); + if (secretRing == null) { + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + } } catch (NotFoundException e) { + // No secret key available (this is what happens most of the time) secretRing = null; } @@ -736,19 +742,19 @@ public class ProviderHelper { // Save the saved keyring (if any) if (secretRing != null) { - log(LogLevel.DEBUG, LogType.MSG_IP_REINSERT_SECRET); progress.setProgress(LogType.MSG_IP_REINSERT_SECRET.getMsgId(), 90, 100); - mIndent += 1; - secretRing = secretRing.canonicalize(mLog, mIndent); - internalSaveSecretKeyRing(secretRing); - result |= SaveKeyringResult.SAVED_SECRET; - mIndent -= 1; + int secretResult = internalSaveSecretKeyRing(secretRing); + if ((secretResult & SaveKeyringResult.RESULT_ERROR) != SaveKeyringResult.RESULT_ERROR) { + result |= SaveKeyringResult.SAVED_SECRET; + } } + mIndent -= 1; return new SaveKeyringResult(result, mLog); } catch (IOException e) { - return null; + log(LogLevel.ERROR, LogType.MSG_IP_FAIL_IO_EXC); + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); } } @@ -759,16 +765,22 @@ public class ProviderHelper { long masterKeyId = secretRing.getMasterKeyId(); log(LogLevel.START, LogType.MSG_IS, new String[]{ PgpKeyHelper.convertKeyIdToHex(masterKeyId) }); - mIndent += 1; - // If there is a secret key, merge it. + // If there is an old secret key, merge it. try { UncachedKeyRing oldSecretRing = getWrappedSecretKeyRing(masterKeyId).getUncached(); - // Merge data from new public ring into the old one + // Merge data from new secret ring into old one secretRing = oldSecretRing.merge(secretRing, mLog, mIndent); + // If this is null, there is an error in the log so we can just return + if (secretRing == null) { + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + } + + // Canonicalize this keyring, to assert a number of assumptions made about it. + secretRing = secretRing.canonicalize(mLog, mIndent); if (secretRing == null) { return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); } @@ -776,26 +788,24 @@ public class ProviderHelper { // Early breakout if nothing changed if (Arrays.hashCode(secretRing.getEncoded()) == Arrays.hashCode(oldSecretRing.getEncoded())) { - log(LogLevel.OK, LogType.MSG_IS, + log(LogLevel.OK, LogType.MSG_IS_SUCCESS_IDENTICAL, new String[]{ PgpKeyHelper.convertKeyIdToHex(masterKeyId) }); return new SaveKeyringResult(SaveKeyringResult.RESULT_OK, mLog); } } catch (NotFoundException e) { - // not an issue, just means we are dealing with a new keyring - } + // Not an issue, just means we are dealing with a new keyring + + // Canonicalize this keyring, to assert a number of assumptions made about it. + secretRing = secretRing.canonicalize(mLog, mIndent); + if (secretRing == null) { + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + } - // Canonicalize this keyring, to assert a number of assumptions made about it. - secretRing = secretRing.canonicalize(mLog, mIndent); - if (secretRing == null) { - return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); } // Merge new data into public keyring as well, if there is any try { UncachedKeyRing oldPublicRing = getWrappedPublicKeyRing(masterKeyId).getUncached(); - log(LogLevel.DEBUG, LogType.MSG_IP_PRESERVING_SECRET); - progress.setProgress(LogType.MSG_IP_PRESERVING_SECRET.getMsgId(), 10, 100); - mIndent += 1; // Merge data from new public ring into secret one UncachedKeyRing publicRing = oldPublicRing.merge(secretRing, mLog, mIndent); @@ -803,7 +813,7 @@ public class ProviderHelper { return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); } - // Early breakout if nothing changed + // If anything changed, reinsert if (Arrays.hashCode(publicRing.getEncoded()) != Arrays.hashCode(oldPublicRing.getEncoded())) { @@ -816,11 +826,12 @@ public class ProviderHelper { } int result = internalSavePublicKeyRing(publicRing, progress, true); + if ((result & SaveKeyringResult.RESULT_ERROR) == SaveKeyringResult.RESULT_ERROR) { + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + } } - mIndent -= 1; - } catch (NotFoundException e) { // TODO, this WILL error out later because secret rings cannot be inserted without // public ones @@ -831,7 +842,8 @@ public class ProviderHelper { return new SaveKeyringResult(result, mLog); } catch (IOException e) { - return null; + log(LogLevel.ERROR, LogType.MSG_IS_FAIL_IO_EXC, null); + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index 4010ab108..48eb39a39 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -131,12 +131,11 @@ public class OperationResultParcel implements Parcelable { MSG_IP_DELETE_OLD_OK (R.string.msg_ip_delete_old_ok), MSG_IP_ENCODE_FAIL (R.string.msg_ip_encode_fail), MSG_IP_FAIL_IO_EXC (R.string.msg_ip_fail_io_exc), - MSG_IP_FAIL_OP_EX (R.string.msg_ip_fail_op_ex), + MSG_IP_FAIL_OP_EXC (R.string.msg_ip_fail_op_exc), MSG_IP_FAIL_REMOTE_EX (R.string.msg_ip_fail_remote_ex), MSG_IP_INSERT_KEYRING (R.string.msg_ip_insert_keyring), MSG_IP_INSERT_SUBKEYS (R.string.msg_ip_insert_keys), MSG_IP_PREPARE (R.string.msg_ip_prepare), - MSG_IP_PRESERVING_SECRET (R.string.msg_ip_preserving_secret), MSG_IP_REINSERT_SECRET (R.string.msg_ip_reinsert_secret), MSG_IP_MASTER (R.string.msg_ip_master), MSG_IP_MASTER_EXPIRED (R.string.msg_ip_master_expired), @@ -161,6 +160,7 @@ public class OperationResultParcel implements Parcelable { MSG_IP_SUBKEY_FLAGS_XXS (R.string.msg_ip_subkey_flags_xxs), MSG_IP_SUBKEY_FLAGS_XXX (R.string.msg_ip_subkey_flags_xxx), MSG_IP_SUCCESS (R.string.msg_ip_success), + MSG_IP_SUCCESS_IDENTICAL (R.string.msg_ip_success_identical), MSG_IP_UID_CERT_BAD (R.string.msg_ip_uid_cert_bad), MSG_IP_UID_CERT_ERROR (R.string.msg_ip_uid_cert_error), MSG_IP_UID_CERT_GOOD (R.string.msg_ip_uid_cert_good), @@ -175,11 +175,12 @@ public class OperationResultParcel implements Parcelable { MSG_IS_BAD_TYPE_PUBLIC (R.string.msg_is_bad_type_public), MSG_IS_DB_EXCEPTION (R.string.msg_is_db_exception), MSG_IS_IMPORTING_SUBKEYS (R.string.msg_is_importing_subkeys), - MSG_IS_IO_EXCPTION (R.string.msg_is_io_excption), + MSG_IS_FAIL_IO_EXC (R.string.msg_is_io_exc), MSG_IS_SUBKEY_NONEXISTENT (R.string.msg_is_subkey_nonexistent), MSG_IS_SUBKEY_OK (R.string.msg_is_subkey_ok), MSG_IS_SUBKEY_STRIPPED (R.string.msg_is_subkey_stripped), MSG_IS_SUCCESS (R.string.msg_is_success), + MSG_IS_SUCCESS_IDENTICAL (R.string.msg_is_success_identical), // keyring canonicalization MSG_KC_PUBLIC (R.string.msg_kc_public), -- cgit v1.2.3 From 3bffe4da559d0ee45c44df1afee6d5b4de9b89d1 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Wed, 18 Jun 2014 20:55:44 +0200 Subject: generate public keyring from secret if not available --- .../keychain/pgp/UncachedKeyRing.java | 16 ++++++++++ .../keychain/provider/ProviderHelper.java | 36 +++++++++++----------- .../keychain/service/OperationResultParcel.java | 5 +-- 3 files changed, 37 insertions(+), 20 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java index a1c6b158b..e1ce62bdf 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java @@ -27,6 +27,7 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.ArrayList; import java.util.Comparator; import java.util.Date; import java.util.HashSet; @@ -759,6 +760,21 @@ public class UncachedKeyRing { } + public UncachedKeyRing extractPublicKeyRing() { + if(!isSecret()) { + throw new RuntimeException("Tried to extract public keyring from non-secret keyring. " + + "This is a programming error and should never happen!"); + } + + ArrayList keys = new ArrayList(); + Iterator it = mRing.getPublicKeys(); + while (it.hasNext()) { + keys.add(it.next()); + } + + return new UncachedKeyRing(new PGPPublicKeyRing(keys)); + } + /** This method replaces a public key in a keyring. * * This method essentially wraps PGP*KeyRing.insertPublicKey, where the keyring may be of either 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 79bd5777c..955fb90ba 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -804,37 +804,37 @@ public class ProviderHelper { } // Merge new data into public keyring as well, if there is any + UncachedKeyRing publicRing; try { UncachedKeyRing oldPublicRing = getWrappedPublicKeyRing(masterKeyId).getUncached(); // Merge data from new public ring into secret one - UncachedKeyRing publicRing = oldPublicRing.merge(secretRing, mLog, mIndent); + publicRing = oldPublicRing.merge(secretRing, mLog, mIndent); if (publicRing == null) { return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); } - // If anything changed, reinsert + // If nothing changed, never mind if (Arrays.hashCode(publicRing.getEncoded()) - != Arrays.hashCode(oldPublicRing.getEncoded())) { - - log(LogLevel.OK, LogType.MSG_IS, - new String[]{ PgpKeyHelper.convertKeyIdToHex(masterKeyId) }); - - publicRing = publicRing.canonicalize(mLog, mIndent); - if (publicRing == null) { - return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); - } + == Arrays.hashCode(oldPublicRing.getEncoded())) { + publicRing = null; + } - int result = internalSavePublicKeyRing(publicRing, progress, true); - if ((result & SaveKeyringResult.RESULT_ERROR) == SaveKeyringResult.RESULT_ERROR) { - return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); - } + } catch (NotFoundException e) { + log(LogLevel.DEBUG, LogType.MSG_IS_PUBRING_GENERATE, null); + publicRing = secretRing.extractPublicKeyRing(); + } + if (publicRing != null) { + publicRing = publicRing.canonicalize(mLog, mIndent); + if (publicRing == null) { + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); } - } catch (NotFoundException e) { - // TODO, this WILL error out later because secret rings cannot be inserted without - // public ones + int result = internalSavePublicKeyRing(publicRing, progress, true); + if ((result & SaveKeyringResult.RESULT_ERROR) == SaveKeyringResult.RESULT_ERROR) { + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + } } progress.setProgress(LogType.MSG_IP_REINSERT_SECRET.getMsgId(), 90, 100); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index 48eb39a39..cfb977911 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -174,13 +174,14 @@ public class OperationResultParcel implements Parcelable { MSG_IS(R.string.msg_is), MSG_IS_BAD_TYPE_PUBLIC (R.string.msg_is_bad_type_public), MSG_IS_DB_EXCEPTION (R.string.msg_is_db_exception), - MSG_IS_IMPORTING_SUBKEYS (R.string.msg_is_importing_subkeys), MSG_IS_FAIL_IO_EXC (R.string.msg_is_io_exc), + MSG_IS_IMPORTING_SUBKEYS (R.string.msg_is_importing_subkeys), + MSG_IS_PUBRING_GENERATE (R.string.msg_is_pubring_generate), MSG_IS_SUBKEY_NONEXISTENT (R.string.msg_is_subkey_nonexistent), MSG_IS_SUBKEY_OK (R.string.msg_is_subkey_ok), MSG_IS_SUBKEY_STRIPPED (R.string.msg_is_subkey_stripped), - MSG_IS_SUCCESS (R.string.msg_is_success), MSG_IS_SUCCESS_IDENTICAL (R.string.msg_is_success_identical), + MSG_IS_SUCCESS (R.string.msg_is_success), // keyring canonicalization MSG_KC_PUBLIC (R.string.msg_kc_public), -- cgit v1.2.3