From 535f2caf2c7dc815a622cfd9ff67fa59214b5da2 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 11 Mar 2014 00:09:11 +0100 Subject: [PATCH 01/30] (preliminary) certs table --- .../keychain/provider/KeychainContract.java | 40 +++++++++++++++++++ .../keychain/provider/KeychainDatabase.java | 22 +++++++++- .../keychain/provider/KeychainProvider.java | 26 ++++++++++++ 3 files changed, 87 insertions(+), 1 deletion(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java index 706b30d05..f9439e338 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java @@ -54,6 +54,16 @@ public class KeychainContract { String RANK = "rank"; } + interface CertsColumns { + String KEY_RING_ROW_ID = "key_ring_row_id"; // verified id, foreign key to key_rings._ID + String RANK = "rank"; // rank of verified key + String KEY_ID = "key_id"; // verified id, not a database id + String KEY_ID_CERTIFIER = "key_id_certifier"; // verifying id, not a database id + String CREATION = "creation"; + String VERIFIED = "verified"; + String KEY_DATA = "key_data"; // certification blob + } + interface ApiAppsColumns { String PACKAGE_NAME = "package_name"; String PACKAGE_SIGNATURE = "package_signature"; @@ -82,6 +92,8 @@ public class KeychainContract { public static final String PATH_BY_MASTER_KEY_ID = "master_key_id"; public static final String PATH_BY_KEY_ID = "key_id"; + public static final String PATH_BY_KEY_ROW_ID = "key_row_id"; + public static final String PATH_BY_CERTIFIER_ID = "certifier_id"; public static final String PATH_BY_EMAILS = "emails"; public static final String PATH_BY_LIKE_EMAIL = "like_email"; @@ -91,6 +103,8 @@ public class KeychainContract { public static final String BASE_API_APPS = "api_apps"; public static final String PATH_BY_PACKAGE_NAME = "package_name"; + public static final String BASE_CERTS = "certs"; + public static class KeyRings implements KeyRingsColumns, BaseColumns { public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() .appendPath(BASE_KEY_RINGS).build(); @@ -260,6 +274,32 @@ public class KeychainContract { } } + public static class Certs implements CertsColumns, BaseColumns { + public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() + .appendPath(BASE_CERTS).build(); + + // do we even need this one...? just using it as default for database insert notifications~ + public static Uri buildCertsUri(String rowId) { + return CONTENT_URI.buildUpon().appendPath(rowId).build(); + } + + public static Uri buildCertsByKeyRowIdUri(String keyRingRowId) { + return CONTENT_URI.buildUpon().appendPath(PATH_BY_KEY_ROW_ID) + .appendPath(keyRingRowId).build(); + } + + public static Uri buildCertsByKeyIdUri(String keyId) { + return CONTENT_URI.buildUpon().appendPath(PATH_BY_KEY_ID).appendPath(keyId) + .build(); + } + + public static Uri buildCertsByCertifierKeyIdUri(String keyId) { + return CONTENT_URI.buildUpon().appendPath(PATH_BY_CERTIFIER_ID).appendPath(keyId) + .build(); + } + + } + public static class DataStream { public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() .appendPath(BASE_DATA).build(); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java index 5f18ed6f9..1a2853dbe 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java @@ -22,6 +22,7 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAppsColumns; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingsColumns; import org.sufficientlysecure.keychain.provider.KeychainContract.KeysColumns; import org.sufficientlysecure.keychain.provider.KeychainContract.UserIdsColumns; +import org.sufficientlysecure.keychain.provider.KeychainContract.CertsColumns; import org.sufficientlysecure.keychain.util.Log; import android.content.Context; @@ -31,13 +32,14 @@ import android.provider.BaseColumns; public class KeychainDatabase extends SQLiteOpenHelper { private static final String DATABASE_NAME = "apg.db"; - private static final int DATABASE_VERSION = 7; + private static final int DATABASE_VERSION = 8; public interface Tables { String KEY_RINGS = "key_rings"; String KEYS = "keys"; String USER_IDS = "user_ids"; String API_APPS = "api_apps"; + String CERTS = "certs"; } private static final String CREATE_KEY_RINGS = "CREATE TABLE IF NOT EXISTS " + Tables.KEY_RINGS @@ -83,6 +85,18 @@ public class KeychainDatabase extends SQLiteOpenHelper { + ApiAppsColumns.HASH_ALORITHM + " INTEGER, " + ApiAppsColumns.COMPRESSION + " INTEGER)"; + private static final String CREATE_CERTS = "CREATE TABLE IF NOT EXISTS " + Tables.CERTS + + " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + + CertsColumns.KEY_RING_ROW_ID + " INTEGER NOT NULL " + + " REFERENCES " + Tables.KEY_RINGS + "(" + BaseColumns._ID + ") ON DELETE CASCADE, " + + CertsColumns.KEY_ID + " INTEGER, " // certified key + + CertsColumns.RANK + " INTEGER, " // key rank of certified uid + + CertsColumns.KEY_ID_CERTIFIER + " INTEGER, " // certifying key + + CertsColumns.CREATION + " INTEGER, " + + CertsColumns.VERIFIED + " INTEGER, " + + CertsColumns.KEY_DATA+ " BLOB)"; + + KeychainDatabase(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } @@ -95,6 +109,7 @@ public class KeychainDatabase extends SQLiteOpenHelper { db.execSQL(CREATE_KEYS); db.execSQL(CREATE_USER_IDS); db.execSQL(CREATE_API_APPS); + db.execSQL(CREATE_CERTS); } @Override @@ -134,6 +149,11 @@ public class KeychainDatabase extends SQLiteOpenHelper { db.execSQL("ALTER TABLE " + Tables.KEYS + " ADD COLUMN " + KeysColumns.FINGERPRINT + " BLOB;"); break; + case 7: + // new table: certs + db.execSQL(CREATE_CERTS); + + break; default: break; diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java index 85e01ae53..bd74de1d7 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -29,6 +29,7 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; import org.sufficientlysecure.keychain.provider.KeychainContract.KeysColumns; import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; import org.sufficientlysecure.keychain.provider.KeychainContract.UserIdsColumns; +import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables; import org.sufficientlysecure.keychain.util.Log; @@ -83,6 +84,11 @@ public class KeychainProvider extends ContentProvider { private static final int UNIFIED_KEY_RING = 401; + private static final int CERTS = 401; + private static final int CERTS_BY_KEY_ID = 402; + private static final int CERTS_BY_ROW_ID = 403; + private static final int CERTS_BY_CERTIFIER_ID = 404; + // private static final int DATA_STREAM = 401; protected UriMatcher mUriMatcher; @@ -238,6 +244,20 @@ public class KeychainProvider extends ContentProvider { matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/" + KeychainContract.PATH_UNIFIED, UNIFIED_KEY_RING); + /** + * certifications + *
+         *
+         * key_rings/unified
+         *
+         */
+        matcher.addURI(authority, KeychainContract.BASE_CERTS, CERTS);
+        matcher.addURI(authority, KeychainContract.BASE_CERTS + "/#", CERTS_BY_ROW_ID);
+        matcher.addURI(authority, KeychainContract.BASE_CERTS + "/"
+                + KeychainContract.PATH_BY_KEY_ID + "/#", CERTS_BY_KEY_ID);
+        matcher.addURI(authority, KeychainContract.BASE_CERTS + "/"
+                + KeychainContract.PATH_BY_CERTIFIER_ID + "/#", CERTS_BY_CERTIFIER_ID);
+
         /**
          * data stream
          *
@@ -783,6 +803,12 @@ public class KeychainProvider extends ContentProvider {
                     rowId = db.insertOrThrow(Tables.API_APPS, null, values);
                     rowUri = ApiApps.buildIdUri(Long.toString(rowId));
 
+                    break;
+                case CERTS_BY_ROW_ID:
+                    rowId = db.insertOrThrow(Tables.CERTS, null, values);
+                    // kinda useless :S
+                    rowUri = Certs.buildCertsUri(Long.toString(rowId));
+
                     break;
                 default:
                     throw new UnsupportedOperationException("Unknown uri: " + uri);

From 74f8ec0365385d61990290b4922dc05d92a4a439 Mon Sep 17 00:00:00 2001
From: Vincent Breitmoser 
Date: Tue, 11 Mar 2014 00:11:27 +0100
Subject: [PATCH 02/30] certs: close in from both sides :)

---
 .../keychain/provider/ProviderHelper.java     | 45 ++++++++++++++++---
 .../keychain/ui/KeyListFragment.java          | 16 ++++---
 2 files changed, 49 insertions(+), 12 deletions(-)

diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
index b6bfc372a..4d1fc7d81 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
@@ -20,6 +20,8 @@ package org.sufficientlysecure.keychain.provider;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.Date;
 
 import org.spongycastle.bcpg.ArmoredOutputStream;
@@ -59,25 +61,30 @@ public class ProviderHelper {
     /**
      * Private helper method to get PGPKeyRing from database
      */
-    public static PGPKeyRing getPGPKeyRing(Context context, Uri queryUri) {
+    public static Map getPGPKeyRings(Context context, Uri queryUri) {
         Cursor cursor = context.getContentResolver().query(queryUri,
-                new String[]{KeyRings._ID, KeyRings.KEY_RING_DATA}, null, null, null);
+                new String[]{KeyRings._ID, KeyRings.MASTER_KEY_ID, KeyRings.KEY_RING_DATA}, null, null, null);
 
-        PGPKeyRing keyRing = null;
-        if (cursor != null && cursor.moveToFirst()) {
+        Map result = new HashMap(cursor.getCount());
+        if (cursor != null && cursor.moveToFirst()) do {
             int keyRingDataCol = cursor.getColumnIndex(KeyRings.KEY_RING_DATA);
+            int masterKeyIdCol = cursor.getColumnIndex(KeyRings.MASTER_KEY_ID);
 
             byte[] data = cursor.getBlob(keyRingDataCol);
             if (data != null) {
-                keyRing = PgpConversionHelper.BytesToPGPKeyRing(data);
+                result.put(cursor.getLong(masterKeyIdCol), PgpConversionHelper.BytesToPGPKeyRing(data));
             }
-        }
+
+        } while(cursor.moveToNext());
 
         if (cursor != null) {
             cursor.close();
         }
 
-        return keyRing;
+        return result;
+    }
+    public static PGPKeyRing getPGPKeyRing(Context context, Uri queryUri) {
+        return getPGPKeyRings(context, queryUri).values().iterator().next();
     }
 
     /**
@@ -321,6 +328,30 @@ public class ProviderHelper {
         return ContentProviderOperation.newInsert(uri).withValues(values).build();
     }
 
+    /**
+     * Build ContentProviderOperation to add PGPPublicKey to database corresponding to a keyRing
+     */
+    private static ContentProviderOperation buildPublicCertOperations(Context context,
+                                                                     long keyRingRowId,
+                                                                     int rank,
+                                                                     long keyId,
+                                                                     PGPSignature cert,
+                                                                     boolean verified)
+            throws IOException {
+        ContentValues values = new ContentValues();
+        values.put(Certs.KEY_RING_ROW_ID, keyRingRowId);
+        values.put(Certs.RANK, rank);
+        values.put(Certs.KEY_ID, keyId);
+        values.put(Certs.KEY_ID_CERTIFIER, cert.getKeyID());
+        values.put(Certs.CREATION, cert.getCreationTime().getTime() / 1000);
+        values.put(Certs.VERIFIED, verified);
+        values.put(Certs.KEY_DATA, cert.getEncoded());
+
+        Uri uri = Certs.buildCertsUri(Long.toString(keyRingRowId));
+
+        return ContentProviderOperation.newInsert(uri).withValues(values).build();
+    }
+
     /**
      * Build ContentProviderOperation to add PublicUserIds to database corresponding to a keyRing
      */
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
index 96b75cf11..ea37677d1 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
@@ -280,7 +280,9 @@ public class KeyListFragment extends Fragment implements AdapterView.OnItemClick
         } else {
             viewIntent = new Intent(getActivity(), ViewKeyActivityJB.class);
         }
-        viewIntent.setData(KeychainContract.KeyRings.buildPublicKeyRingsByMasterKeyIdUri(Long.toString(mAdapter.getMasterKeyId(position))));
+        viewIntent.setData(KeychainContract.KeyRings.buildPublicKeyRingsByMasterKeyIdUri(
+                Long.toString(mAdapter.getMasterKeyId(position)))
+        );
         startActivity(viewIntent);
     }
 
@@ -379,8 +381,8 @@ public class KeyListFragment extends Fragment implements AdapterView.OnItemClick
         /**
          * Bind cursor data to the item list view
          * 

- * NOTE: CursorAdapter already implements the ViewHolder pattern in its getView() method. Thus - * no ViewHolder is required here. + * NOTE: CursorAdapter already implements the ViewHolder pattern in its getView() method. + * Thus no ViewHolder is required here. */ @Override public void bindView(View view, Context context, Cursor cursor) { @@ -417,7 +419,11 @@ public class KeyListFragment extends Fragment implements AdapterView.OnItemClick button.setOnClickListener(new OnClickListener() { public void onClick(View view) { Intent editIntent = new Intent(getActivity(), EditKeyActivity.class); - editIntent.setData(KeychainContract.KeyRings.buildSecretKeyRingsByMasterKeyIdUri(Long.toString(id))); + editIntent.setData( + KeychainContract.KeyRings.buildSecretKeyRingsByMasterKeyIdUri( + Long.toString(id) + ) + ); editIntent.setAction(EditKeyActivity.ACTION_EDIT_KEY); startActivityForResult(editIntent, 0); } @@ -504,7 +510,7 @@ public class KeyListFragment extends Fragment implements AdapterView.OnItemClick String userId = mCursor.getString(KeyListFragment.INDEX_UID); String headerText = convertView.getResources().getString(R.string.user_id_no_name); if (userId != null && userId.length() > 0) { - headerText = "" + mCursor.getString(KeyListFragment.INDEX_UID).subSequence(0, 1).charAt(0); + headerText = "" + userId.subSequence(0, 1).charAt(0); } holder.text.setText(headerText); holder.count.setVisibility(View.GONE); From ba7613dc3462cc9ce5ff2454b6ab086c44bc22a1 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 11 Mar 2014 00:13:03 +0100 Subject: [PATCH 03/30] experimental cert import --- .../keychain/provider/ProviderHelper.java | 49 ++++++++++++++++--- 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index 4d1fc7d81..981235030 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -19,6 +19,7 @@ package org.sufficientlysecure.keychain.provider; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.security.SignatureException; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; @@ -27,12 +28,14 @@ import java.util.Date; import org.spongycastle.bcpg.ArmoredOutputStream; import org.spongycastle.bcpg.UserAttributePacket; import org.spongycastle.bcpg.UserAttributeSubpacket; +import org.spongycastle.openpgp.PGPException; import org.spongycastle.openpgp.PGPKeyRing; 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.operator.jcajce.JcaPGPContentVerifierBuilderProvider; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.pgp.PgpConversionHelper; import org.sufficientlysecure.keychain.pgp.PgpHelper; @@ -41,6 +44,7 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; +import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables; import org.sufficientlysecure.keychain.service.remote.AppSettings; import org.sufficientlysecure.keychain.util.IterableIterator; @@ -214,19 +218,50 @@ public class ProviderHelper { ++rank; } + // get a list of owned secret keys, for verification filtering + Map allKeyRings = getPGPKeyRings(context, KeyRings.buildPublicKeyRingsUri()); + int userIdRank = 0; for (String userId : new IterableIterator(masterKey.getUserIDs())) { operations.add(buildPublicUserIdOperations(context, keyRingRowId, userId, userIdRank)); + + // look through signatures for this specific key + for (PGPSignature cert : new IterableIterator( + masterKey.getSignaturesForID(userId))) { + long certId = cert.getKeyID(); + boolean verified = false; + // only care for signatures from our own private keys + if(allKeyRings.containsKey(certId)) try { + // mark them as verified + cert.init(new JcaPGPContentVerifierBuilderProvider().setProvider("SC"), allKeyRings.get(certId).getPublicKey()); + verified = cert.verifyCertification(userId, masterKey); + // TODO: at this point, we only save signatures from available secret keys. + // should we save all? those are quite a lot of rows for info we don't really + // use. I left it out for now - it is available from key servers, so we can + // always get it later. + Log.d(Constants.TAG, "sig for " + userId + " " + verified + " from " + + PgpKeyHelper.convertKeyIdToHex(cert.getKeyID()) + ); + operations.add(buildPublicCertOperations( + context, keyRingRowId, userIdRank, masterKey.getKeyID(), cert, verified)); + } catch(SignatureException e) { + Log.e(Constants.TAG, "Signature verification failed.", e); + } catch(PGPException e) { + Log.e(Constants.TAG, "Signature verification failed.", e); + } else { + Log.d(Constants.TAG, "ignored sig for " + + PgpKeyHelper.convertKeyIdToHex(masterKey.getKeyID()) + + " from " + + PgpKeyHelper.convertKeyIdToHex(cert.getKeyID()) + ); + } + // if we wanted to save all, not just our own verifications + // buildPublicCertOperations(context, keyRingRowId, rank, cert, verified); + } + ++userIdRank; } - for (PGPSignature certification : new IterableIterator(masterKey.getSignaturesOfType(PGPSignature.POSITIVE_CERTIFICATION))) { - //TODO: how to do this?? we need to verify the signatures again and again when they are displayed... -// if (certification.verify -// operations.add(buildPublicKeyOperations(context, keyRingRowId, key, rank)); - } - - try { context.getContentResolver().applyBatch(KeychainContract.CONTENT_AUTHORITY, operations); } catch (RemoteException e) { From 7a8db90425dc32ab8f31eb609bfe73999e6dec3e Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 11 Mar 2014 00:14:04 +0100 Subject: [PATCH 04/30] join cert info into query routines --- .../keychain/provider/KeychainProvider.java | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java index bd74de1d7..095887433 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -485,6 +485,8 @@ public class KeychainProvider extends ContentProvider { SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); SQLiteDatabase db = mApgDatabase.getReadableDatabase(); + String groupBy = null; + int match = mUriMatcher.match(uri); // screw that switch @@ -677,8 +679,25 @@ public class KeychainProvider extends ContentProvider { case PUBLIC_KEY_RING_USER_ID: case SECRET_KEY_RING_USER_ID: - qb.setTables(Tables.USER_IDS); - qb.appendWhere(UserIdsColumns.KEY_RING_ROW_ID + " = "); + qb.setTables(Tables.USER_IDS + + " LEFT JOIN " + Tables.CERTS + + " ON (" + + Tables.USER_IDS + "." + UserIds.KEY_RING_ROW_ID + " = " + + Tables.CERTS + "." + Certs.KEY_RING_ROW_ID + + " AND " + Tables.USER_IDS + "." + UserIds.RANK + " = " + + Tables.CERTS + "." + Certs.RANK + + ")"); + + groupBy = Tables.USER_IDS + "." + UserIds.RANK; + + HashMap pmap = new HashMap(); + pmap.put(UserIds._ID, Tables.USER_IDS + "." + UserIds._ID); + pmap.put(UserIds.USER_ID, Tables.USER_IDS + "." + UserIds.USER_ID); + pmap.put(UserIds.RANK, Tables.USER_IDS + "." + UserIds.RANK); + pmap.put("verified", "COUNT(" + Tables.CERTS + "." + Certs._ID + ") AS verified"); + qb.setProjectionMap(pmap); + + qb.appendWhere(Tables.USER_IDS + "." + UserIdsColumns.KEY_RING_ROW_ID + " = "); qb.appendWhereEscapeString(uri.getPathSegments().get(2)); break; @@ -725,7 +744,7 @@ public class KeychainProvider extends ContentProvider { orderBy = sortOrder; } - Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, orderBy); + Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, null, orderBy); // Tell the cursor what uri to watch, so it knows when its source data changes c.setNotificationUri(getContext().getContentResolver(), uri); From 3b38ffe55eef7a3a9a0cd4367c7f7ee42e4305b8 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 11 Mar 2014 00:14:38 +0100 Subject: [PATCH 05/30] experimental cert display --- .../keychain/ui/ViewKeyMainFragment.java | 7 ++++--- .../keychain/ui/adapter/ViewKeyUserIdsAdapter.java | 5 ++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java index c7403b0a0..6a87beb31 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java @@ -39,6 +39,7 @@ import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.provider.KeychainContract; +import org.sufficientlysecure.keychain.provider.KeychainDatabase; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.ui.adapter.ViewKeyKeysAdapter; import org.sufficientlysecure.keychain.ui.adapter.ViewKeyUserIdsAdapter; @@ -173,10 +174,10 @@ public class ViewKeyMainFragment extends Fragment implements static final int KEYRING_INDEX_MASTER_KEY_ID = 1; static final int KEYRING_INDEX_USER_ID = 2; - static final String[] USER_IDS_PROJECTION = new String[]{KeychainContract.UserIds._ID, KeychainContract.UserIds.USER_ID, - KeychainContract.UserIds.RANK,}; + static final String[] USER_IDS_PROJECTION = new String[]{ KeychainContract.UserIds._ID, KeychainContract.UserIds.USER_ID, + KeychainContract.UserIds.RANK, "verified" }; // not the main user id - static final String USER_IDS_SELECTION = KeychainContract.UserIds.RANK + " > 0 "; + static final String USER_IDS_SELECTION = KeychainDatabase.Tables.USER_IDS + "." + KeychainContract.UserIds.RANK + " > 0 "; static final String USER_IDS_SORT_ORDER = KeychainContract.UserIds.USER_ID + " COLLATE LOCALIZED ASC"; static final String[] KEYS_PROJECTION = new String[]{KeychainContract.Keys._ID, KeychainContract.Keys.KEY_ID, diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java index cf8699417..5a991ce11 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java @@ -32,6 +32,7 @@ public class ViewKeyUserIdsAdapter extends CursorAdapter { private LayoutInflater mInflater; private int mIndexUserId; + private int mVerifiedId; public ViewKeyUserIdsAdapter(Context context, Cursor c, int flags) { super(context, c, flags); @@ -57,15 +58,17 @@ public class ViewKeyUserIdsAdapter extends CursorAdapter { private void initIndex(Cursor cursor) { if (cursor != null) { mIndexUserId = cursor.getColumnIndexOrThrow(UserIds.USER_ID); + mVerifiedId = cursor.getColumnIndexOrThrow("verified"); } } @Override public void bindView(View view, Context context, Cursor cursor) { String userIdStr = cursor.getString(mIndexUserId); + int verified = cursor.getInt(mVerifiedId); TextView userId = (TextView) view.findViewById(R.id.userId); - userId.setText(userIdStr); + userId.setText(userIdStr + (verified > 0 ? " (ok)" : "(nope)")); } @Override From 74027abdad96069a3b1af9ac2d2711beff07f76f Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 11 Mar 2014 02:40:41 +0100 Subject: [PATCH 06/30] experimental cert list, proper --- .../keychain/provider/KeychainProvider.java | 45 +++- .../keychain/ui/ViewKeyActivity.java | 14 +- .../keychain/ui/ViewKeyCertsFragment.java | 245 ++++++++++++++++-- .../res/layout/view_key_certs_fragment.xml | 29 +-- 4 files changed, 275 insertions(+), 58 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java index 095887433..e582ec559 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -87,7 +87,8 @@ public class KeychainProvider extends ContentProvider { private static final int CERTS = 401; private static final int CERTS_BY_KEY_ID = 402; private static final int CERTS_BY_ROW_ID = 403; - private static final int CERTS_BY_CERTIFIER_ID = 404; + private static final int CERTS_BY_KEY_ROW_ID = 404; + private static final int CERTS_BY_CERTIFIER_ID = 405; // private static final int DATA_STREAM = 401; @@ -253,6 +254,8 @@ public class KeychainProvider extends ContentProvider { */ matcher.addURI(authority, KeychainContract.BASE_CERTS, CERTS); matcher.addURI(authority, KeychainContract.BASE_CERTS + "/#", CERTS_BY_ROW_ID); + matcher.addURI(authority, KeychainContract.BASE_CERTS + "/" + + KeychainContract.PATH_BY_KEY_ROW_ID + "/#", CERTS_BY_KEY_ROW_ID); matcher.addURI(authority, KeychainContract.BASE_CERTS + "/" + KeychainContract.PATH_BY_KEY_ID + "/#", CERTS_BY_KEY_ID); matcher.addURI(authority, KeychainContract.BASE_CERTS + "/" @@ -713,6 +716,44 @@ public class KeychainProvider extends ContentProvider { break; + case CERTS_BY_KEY_ROW_ID: + qb.setTables(Tables.CERTS + + " JOIN " + Tables.USER_IDS + " ON (" + + Tables.CERTS + "." + Certs.KEY_RING_ROW_ID + " = " + + Tables.USER_IDS + "." + UserIds.KEY_RING_ROW_ID + + " AND " + + Tables.CERTS + "." + Certs.RANK + " = " + + Tables.USER_IDS + "." + UserIds.RANK + // noooooooot sure about this~ database design + + ") LEFT JOIN " + Tables.KEYS + " ON (" + + Tables.CERTS + "." + Certs.KEY_ID_CERTIFIER + " = " + + Tables.KEYS + "." + Keys.KEY_ID + + ") LEFT JOIN " + Tables.USER_IDS + " AS signer ON (" + + Tables.KEYS + "." + Keys.KEY_RING_ROW_ID + " = " + + "signer." + UserIds.KEY_RING_ROW_ID + + " AND " + + Tables.KEYS + "." + Keys.RANK + " = " + + "signer." + UserIds.RANK + + ")"); + + // groupBy = Tables.USER_IDS + "." + UserIds.RANK; + + HashMap pmap2 = new HashMap(); + pmap2.put(Certs._ID, Tables.CERTS + "." + Certs._ID); + pmap2.put(Certs.RANK, Tables.CERTS + "." + Certs.RANK); + pmap2.put(Certs.KEY_ID_CERTIFIER, Tables.CERTS + "." + Certs.KEY_ID_CERTIFIER); + pmap2.put(Certs.VERIFIED, Tables.CERTS + "." + Certs.VERIFIED); + // verified key data + pmap2.put(UserIds.USER_ID, Tables.USER_IDS + "." + UserIds.USER_ID); + // verifying key data + pmap2.put("signer_uid", "signer." + UserIds.USER_ID + " AS signer_uid"); + qb.setProjectionMap(pmap2); + + qb.appendWhere(Tables.CERTS + "." + Certs.KEY_RING_ROW_ID + " = "); + qb.appendWhereEscapeString(uri.getPathSegments().get(2)); + + break; + case API_APPS: qb.setTables(Tables.API_APPS); @@ -825,7 +866,7 @@ public class KeychainProvider extends ContentProvider { break; case CERTS_BY_ROW_ID: rowId = db.insertOrThrow(Tables.CERTS, null, values); - // kinda useless :S + // kinda useless.. should this be buildCertsByKeyRowIdUri? rowUri = Certs.buildCertsUri(Long.toString(rowId)); break; diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java index 8f177dc6b..055203c0f 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -84,13 +84,11 @@ public class ViewKeyActivity extends ActionBarActivity { selectedTab = intent.getExtras().getInt(EXTRA_SELECTED_TAB); } - { - // normalize mDataUri to a "by row id" query, to ensure it works with any - // given valid /public/ query - long rowId = ProviderHelper.getRowId(this, getIntent().getData()); - // TODO: handle (rowId == 0) with something else than a crash - mDataUri = KeychainContract.KeyRings.buildPublicKeyRingsUri(Long.toString(rowId)) ; - } + // normalize mDataUri to a "by row id" query, to ensure it works with any + // given valid /public/ query + long rowId = ProviderHelper.getRowId(this, getIntent().getData()); + // TODO: handle (rowId == 0) with something else than a crash + mDataUri = KeychainContract.KeyRings.buildPublicKeyRingsUri(Long.toString(rowId)) ; Bundle mainBundle = new Bundle(); mainBundle.putParcelable(ViewKeyMainFragment.ARG_DATA_URI, mDataUri); @@ -98,7 +96,7 @@ public class ViewKeyActivity extends ActionBarActivity { ViewKeyMainFragment.class, mainBundle, (selectedTab == 0 ? true : false)); Bundle certBundle = new Bundle(); - certBundle.putParcelable(ViewKeyCertsFragment.ARG_DATA_URI, mDataUri); + certBundle.putLong(ViewKeyCertsFragment.ARG_KEYRING_ROW_ID, rowId); mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.key_view_tab_certs)), ViewKeyCertsFragment.class, certBundle, (selectedTab == 1 ? true : false)); } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java index 36d3e6ace..abed097c1 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java @@ -17,26 +17,66 @@ package org.sufficientlysecure.keychain.ui; +import android.annotation.SuppressLint; +import android.content.Context; import android.content.Intent; +import android.database.Cursor; +import android.graphics.Color; import android.net.Uri; import android.os.Bundle; import android.support.v4.app.Fragment; +import android.support.v4.app.LoaderManager; +import android.support.v4.content.CursorLoader; +import android.support.v4.content.Loader; +import android.support.v4.widget.CursorAdapter; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.Button; +import android.widget.TextView; import com.beardedhen.androidbootstrap.BootstrapButton; import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.Id; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; +import org.sufficientlysecure.keychain.provider.KeychainContract; +import org.sufficientlysecure.keychain.provider.KeychainDatabase; import org.sufficientlysecure.keychain.util.Log; +import java.nio.ByteBuffer; +import java.util.HashMap; -public class ViewKeyCertsFragment extends Fragment { +import se.emilsjolander.stickylistheaders.ApiLevelTooLowException; +import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter; +import se.emilsjolander.stickylistheaders.StickyListHeadersListView; - public static final String ARG_DATA_URI = "uri"; - private BootstrapButton mActionCertify; +public class ViewKeyCertsFragment extends Fragment + implements LoaderManager.LoaderCallbacks { + + // These are the rows that we will retrieve. + static final String[] PROJECTION = new String[]{ + KeychainContract.Certs._ID, + KeychainContract.Certs.VERIFIED, + KeychainContract.Certs.RANK, + KeychainContract.Certs.KEY_ID_CERTIFIER, + KeychainContract.UserIds.USER_ID, + "signer_uid" + }; + + // sort by our user id, + static final String SORT_ORDER = + KeychainDatabase.Tables.USER_IDS + "." + KeychainContract.UserIds.USER_ID + " ASC, " + + KeychainDatabase.Tables.CERTS + "." + KeychainContract.Certs.VERIFIED + " DESC, " + + "signer_uid ASC"; + + public static final String ARG_KEYRING_ROW_ID = "row_id"; + + private StickyListHeadersListView mStickyList; + + private CertListAdapter mAdapter; private Uri mDataUri; @@ -44,8 +84,6 @@ public class ViewKeyCertsFragment extends Fragment { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.view_key_certs_fragment, container, false); - mActionCertify = (BootstrapButton) view.findViewById(R.id.action_certify); - return view; } @@ -53,40 +91,197 @@ public class ViewKeyCertsFragment extends Fragment { public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - Uri dataUri = getArguments().getParcelable(ARG_DATA_URI); - if (dataUri == null) { + mStickyList = (StickyListHeadersListView) getActivity().findViewById(R.id.list); + + if (!getArguments().containsKey(ARG_KEYRING_ROW_ID)) { Log.e(Constants.TAG, "Data missing. Should be Uri of key!"); getActivity().finish(); return; } - loadData(dataUri); - } + long rowId = getArguments().getLong(ARG_KEYRING_ROW_ID); + mDataUri = KeychainContract.Certs.buildCertsByKeyRowIdUri(Long.toString(rowId)); - private void loadData(Uri dataUri) { - if (dataUri.equals(mDataUri)) { - Log.d(Constants.TAG, "Same URI, no need to load the data again!"); - return; + mStickyList.setAreHeadersSticky(true); + mStickyList.setDrawingListUnderStickyHeader(false); + mStickyList.setFastScrollEnabled(true); + + try { + mStickyList.setFastScrollAlwaysVisible(true); + } catch (ApiLevelTooLowException e) { } - mDataUri = dataUri; + // TODO this view is made visible if no data is available + // mStickyList.setEmptyView(getActivity().findViewById(R.id.empty)); - Log.i(Constants.TAG, "mDataUri: " + mDataUri.toString()); - mActionCertify.setOnClickListener(new View.OnClickListener() { + // Create an empty adapter we will use to display the loaded data. + mAdapter = new CertListAdapter(getActivity(), null); + mStickyList.setAdapter(mAdapter); - @Override - public void onClick(View v) { - certifyKey(mDataUri); - } - }); + // Prepare the loader. Either re-connect with an existing one, + // or start a new one. + getLoaderManager().initLoader(0, null, this); } - private void certifyKey(Uri dataUri) { - Intent signIntent = new Intent(getActivity(), CertifyKeyActivity.class); - signIntent.setData(dataUri); - startActivity(signIntent); + @Override + public Loader onCreateLoader(int id, Bundle args) { + // Now create and return a CursorLoader that will take care of + // creating a Cursor for the data being displayed. + return new CursorLoader(getActivity(), mDataUri, PROJECTION, null, null, SORT_ORDER); + } + + @Override + public void onLoadFinished(Loader loader, Cursor data) { + // Swap the new cursor in. (The framework will take care of closing the + // old cursor once we return.) + mAdapter.swapCursor(data); + + mStickyList.setAdapter(mAdapter); + } + + @Override + public void onLoaderReset(Loader loader) { + // This is called when the last Cursor provided to onLoadFinished() + // above is about to be closed. We need to make sure we are no + // longer using it. + mAdapter.swapCursor(null); + } + + /** + * Implements StickyListHeadersAdapter from library + */ + private class CertListAdapter extends CursorAdapter implements StickyListHeadersAdapter { + private LayoutInflater mInflater; + private int mIndexUserId, mIndexRank; + private int mIndexSignerKeyId, mIndexSignerUserId; + private int mIndexVerified; + + public CertListAdapter(Context context, Cursor c) { + super(context, c, 0); + + mInflater = LayoutInflater.from(context); + initIndex(c); + } + + @Override + public Cursor swapCursor(Cursor newCursor) { + initIndex(newCursor); + + return super.swapCursor(newCursor); + } + + /** + * Get column indexes for performance reasons just once in constructor and swapCursor. For a + * performance comparison see http://stackoverflow.com/a/17999582 + * + * @param cursor + */ + private void initIndex(Cursor cursor) { + if (cursor != null) { + + mIndexUserId = cursor.getColumnIndexOrThrow(KeychainContract.UserIds.USER_ID); + mIndexRank = cursor.getColumnIndexOrThrow(KeychainContract.UserIds.RANK); + mIndexVerified = cursor.getColumnIndexOrThrow(KeychainContract.Certs.VERIFIED); + mIndexSignerKeyId = cursor.getColumnIndexOrThrow(KeychainContract.Certs.KEY_ID_CERTIFIER); + mIndexSignerUserId = cursor.getColumnIndexOrThrow("signer_uid"); + } + } + + /** + * Bind cursor data to the item list view + *

+ * NOTE: CursorAdapter already implements the ViewHolder pattern in its getView() method. + * Thus no ViewHolder is required here. + */ + @Override + public void bindView(View view, Context context, Cursor cursor) { + + // set name and stuff, common to both key types + TextView wSignerKeyId = (TextView) view.findViewById(R.id.signerKeyId); + TextView wSignerUserId = (TextView) view.findViewById(R.id.signerUserId); + TextView wSignStatus = (TextView) view.findViewById(R.id.signStatus); + + String signerKeyId = PgpKeyHelper.convertKeyIdToHex(cursor.getLong(mIndexSignerKeyId)); + String signerUserId = cursor.getString(mIndexSignerUserId); + String signStatus = cursor.getInt(mIndexVerified) > 0 ? "ok" : "unknown"; + + wSignerUserId.setText(signerUserId); + wSignerKeyId.setText(signerKeyId); + wSignStatus.setText(signStatus); + + } + + @Override + public View newView(Context context, Cursor cursor, ViewGroup parent) { + return mInflater.inflate(R.layout.view_key_certs_item, parent, false); + } + + /** + * Creates a new header view and binds the section headers to it. It uses the ViewHolder + * pattern. Most functionality is similar to getView() from Android's CursorAdapter. + *

+ * NOTE: The variables mDataValid and mCursor are available due to the super class + * CursorAdapter. + */ + @Override + public View getHeaderView(int position, View convertView, ViewGroup parent) { + HeaderViewHolder holder; + if (convertView == null) { + holder = new HeaderViewHolder(); + convertView = mInflater.inflate(R.layout.view_key_certs_header, parent, false); + holder.text = (TextView) convertView.findViewById(R.id.stickylist_header_text); + holder.count = (TextView) convertView.findViewById(R.id.certs_num); + convertView.setTag(holder); + } else { + holder = (HeaderViewHolder) convertView.getTag(); + } + + if (!mDataValid) { + // no data available at this point + Log.d(Constants.TAG, "getHeaderView: No data available at this point!"); + return convertView; + } + + if (!mCursor.moveToPosition(position)) { + throw new IllegalStateException("couldn't move cursor to position " + position); + } + + // set header text as first char in user id + String userId = mCursor.getString(mIndexUserId); + holder.text.setText(userId); + holder.count.setVisibility(View.GONE); + return convertView; + } + + /** + * Header IDs should be static, position=1 should always return the same Id that is. + */ + @Override + public long getHeaderId(int position) { + if (!mDataValid) { + // no data available at this point + Log.d(Constants.TAG, "getHeaderView: No data available at this point!"); + return -1; + } + + if (!mCursor.moveToPosition(position)) { + throw new IllegalStateException("couldn't move cursor to position " + position); + } + + // otherwise, return the first character of the name as ID + return mCursor.getInt(mIndexRank); + + // sort by the first four characters (should be enough I guess?) + // return ByteBuffer.wrap(userId.getBytes()).asLongBuffer().get(0); + } + + class HeaderViewHolder { + TextView text; + TextView count; + } + } } \ No newline at end of file diff --git a/OpenPGP-Keychain/src/main/res/layout/view_key_certs_fragment.xml b/OpenPGP-Keychain/src/main/res/layout/view_key_certs_fragment.xml index 299471c66..faa26b525 100644 --- a/OpenPGP-Keychain/src/main/res/layout/view_key_certs_fragment.xml +++ b/OpenPGP-Keychain/src/main/res/layout/view_key_certs_fragment.xml @@ -1,7 +1,8 @@ + android:layout_height="match_parent" + android:fillViewport="true"> - - - - - + android:layout_height="match_parent" + class="se.emilsjolander.stickylistheaders.StickyListHeadersListView" + android:id="@+id/list" /> \ No newline at end of file From 8b4a63d5c445abadb83d041490283d05070ff955 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 11 Mar 2014 02:40:57 +0100 Subject: [PATCH 07/30] add keys we don't know about, just for fun --- .../sufficientlysecure/keychain/provider/ProviderHelper.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index 981235030..c1b2ef6e3 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -254,6 +254,8 @@ public class ProviderHelper { + " from " + PgpKeyHelper.convertKeyIdToHex(cert.getKeyID()) ); + operations.add(buildPublicCertOperations( + context, keyRingRowId, userIdRank, masterKey.getKeyID(), cert, false)); } // if we wanted to save all, not just our own verifications // buildPublicCertOperations(context, keyRingRowId, rank, cert, verified); From 3ed93b767143909c2ebf275047d38f06a18c2629 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 11 Mar 2014 03:19:12 +0100 Subject: [PATCH 08/30] add "show unknown signatures" button --- .../keychain/provider/KeychainProvider.java | 13 ++++--- .../keychain/ui/ViewKeyCertsFragment.java | 34 +++++++++++++------ .../res/layout/view_key_certs_fragment.xml | 29 ++++++++++++---- .../src/main/res/values/strings.xml | 1 + 4 files changed, 56 insertions(+), 21 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java index e582ec559..765bd1c44 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -88,7 +88,8 @@ public class KeychainProvider extends ContentProvider { private static final int CERTS_BY_KEY_ID = 402; private static final int CERTS_BY_ROW_ID = 403; private static final int CERTS_BY_KEY_ROW_ID = 404; - private static final int CERTS_BY_CERTIFIER_ID = 405; + private static final int CERTS_BY_KEY_ROW_ID_ALL = 405; + private static final int CERTS_BY_CERTIFIER_ID = 406; // private static final int DATA_STREAM = 401; @@ -256,6 +257,8 @@ public class KeychainProvider extends ContentProvider { matcher.addURI(authority, KeychainContract.BASE_CERTS + "/#", CERTS_BY_ROW_ID); matcher.addURI(authority, KeychainContract.BASE_CERTS + "/" + KeychainContract.PATH_BY_KEY_ROW_ID + "/#", CERTS_BY_KEY_ROW_ID); + matcher.addURI(authority, KeychainContract.BASE_CERTS + "/" + + KeychainContract.PATH_BY_KEY_ROW_ID + "/#/all", CERTS_BY_KEY_ROW_ID_ALL); matcher.addURI(authority, KeychainContract.BASE_CERTS + "/" + KeychainContract.PATH_BY_KEY_ID + "/#", CERTS_BY_KEY_ID); matcher.addURI(authority, KeychainContract.BASE_CERTS + "/" @@ -717,6 +720,7 @@ public class KeychainProvider extends ContentProvider { break; case CERTS_BY_KEY_ROW_ID: + case CERTS_BY_KEY_ROW_ID_ALL: qb.setTables(Tables.CERTS + " JOIN " + Tables.USER_IDS + " ON (" + Tables.CERTS + "." + Certs.KEY_RING_ROW_ID + " = " @@ -725,9 +729,10 @@ public class KeychainProvider extends ContentProvider { + Tables.CERTS + "." + Certs.RANK + " = " + Tables.USER_IDS + "." + UserIds.RANK // noooooooot sure about this~ database design - + ") LEFT JOIN " + Tables.KEYS + " ON (" - + Tables.CERTS + "." + Certs.KEY_ID_CERTIFIER + " = " - + Tables.KEYS + "." + Keys.KEY_ID + + ")" + (match == CERTS_BY_KEY_ROW_ID_ALL ? " LEFT" : "") + + " JOIN " + Tables.KEYS + " ON (" + + Tables.CERTS + "." + Certs.KEY_ID_CERTIFIER + " = " + + Tables.KEYS + "." + Keys.KEY_ID + ") LEFT JOIN " + Tables.USER_IDS + " AS signer ON (" + Tables.KEYS + "." + Keys.KEY_RING_ROW_ID + " = " + "signer." + UserIds.KEY_RING_ROW_ID diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java index abed097c1..3158ca713 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java @@ -19,9 +19,7 @@ package org.sufficientlysecure.keychain.ui; import android.annotation.SuppressLint; import android.content.Context; -import android.content.Intent; import android.database.Cursor; -import android.graphics.Color; import android.net.Uri; import android.os.Bundle; import android.support.v4.app.Fragment; @@ -32,22 +30,17 @@ import android.support.v4.widget.CursorAdapter; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.Button; +import android.widget.CheckBox; +import android.widget.CompoundButton; import android.widget.TextView; -import com.beardedhen.androidbootstrap.BootstrapButton; - import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.Id; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainDatabase; import org.sufficientlysecure.keychain.util.Log; -import java.nio.ByteBuffer; -import java.util.HashMap; - import se.emilsjolander.stickylistheaders.ApiLevelTooLowException; import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter; import se.emilsjolander.stickylistheaders.StickyListHeadersListView; @@ -75,10 +68,12 @@ public class ViewKeyCertsFragment extends Fragment public static final String ARG_KEYRING_ROW_ID = "row_id"; private StickyListHeadersListView mStickyList; + private CheckBox mShowUnknown; private CertListAdapter mAdapter; + private boolean mUnknownShown = false; - private Uri mDataUri; + private Uri mBaseUri, mDataUri; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -87,10 +82,26 @@ public class ViewKeyCertsFragment extends Fragment return view; } + private void toggleShowUnknown(boolean shown) { + if(shown) + mDataUri = mBaseUri.buildUpon().appendPath("all").build(); + else + mDataUri = mBaseUri; + getLoaderManager().restartLoader(0, null, this); + } + @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); + mShowUnknown = (CheckBox) getActivity().findViewById(R.id.showUnknown); + mShowUnknown.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton compoundButton, boolean b) { + toggleShowUnknown(b); + } + }); + mStickyList = (StickyListHeadersListView) getActivity().findViewById(R.id.list); if (!getArguments().containsKey(ARG_KEYRING_ROW_ID)) { @@ -100,7 +111,7 @@ public class ViewKeyCertsFragment extends Fragment } long rowId = getArguments().getLong(ARG_KEYRING_ROW_ID); - mDataUri = KeychainContract.Certs.buildCertsByKeyRowIdUri(Long.toString(rowId)); + mBaseUri = KeychainContract.Certs.buildCertsByKeyRowIdUri(Long.toString(rowId)); mStickyList.setAreHeadersSticky(true); mStickyList.setDrawingListUnderStickyHeader(false); @@ -121,6 +132,7 @@ public class ViewKeyCertsFragment extends Fragment // Prepare the loader. Either re-connect with an existing one, // or start a new one. + mDataUri = mBaseUri; getLoaderManager().initLoader(0, null, this); } diff --git a/OpenPGP-Keychain/src/main/res/layout/view_key_certs_fragment.xml b/OpenPGP-Keychain/src/main/res/layout/view_key_certs_fragment.xml index faa26b525..40db5e766 100644 --- a/OpenPGP-Keychain/src/main/res/layout/view_key_certs_fragment.xml +++ b/OpenPGP-Keychain/src/main/res/layout/view_key_certs_fragment.xml @@ -4,18 +4,35 @@ android:layout_height="match_parent" android:fillViewport="true"> - + android:orientation="vertical"> - + android:id="@+id/list" + android:layout_alignParentTop="true" + android:layout_above="@+id/showUnknown" + android:paddingRight="32dp" + android:layout_alignParentLeft="true" + android:layout_alignParentStart="true" + android:paddingLeft="16dp" /> + + + + \ No newline at end of file diff --git a/OpenPGP-Keychain/src/main/res/values/strings.xml b/OpenPGP-Keychain/src/main/res/values/strings.xml index e13b9e0c6..42f74fda2 100644 --- a/OpenPGP-Keychain/src/main/res/values/strings.xml +++ b/OpenPGP-Keychain/src/main/res/values/strings.xml @@ -442,5 +442,6 @@ Secret Key available unavailable + Show unknown signatures From 57873650b1b0ccdfb949a5e20a6ac039504a2461 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 11 Mar 2014 03:19:41 +0100 Subject: [PATCH 09/30] forgot to commit two layout files --- .../main/res/layout/view_key_certs_header.xml | 30 ++++++++++++ .../main/res/layout/view_key_certs_item.xml | 46 +++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 OpenPGP-Keychain/src/main/res/layout/view_key_certs_header.xml create mode 100644 OpenPGP-Keychain/src/main/res/layout/view_key_certs_item.xml diff --git a/OpenPGP-Keychain/src/main/res/layout/view_key_certs_header.xml b/OpenPGP-Keychain/src/main/res/layout/view_key_certs_header.xml new file mode 100644 index 000000000..de481c1cc --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/layout/view_key_certs_header.xml @@ -0,0 +1,30 @@ + + + + + + + + \ No newline at end of file diff --git a/OpenPGP-Keychain/src/main/res/layout/view_key_certs_item.xml b/OpenPGP-Keychain/src/main/res/layout/view_key_certs_item.xml new file mode 100644 index 000000000..de7570818 --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/layout/view_key_certs_item.xml @@ -0,0 +1,46 @@ + + + + + + + + + + From 84598dd66575a767f1061c03807eae741ce5199f Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Wed, 12 Mar 2014 02:11:16 +0100 Subject: [PATCH 10/30] add view cert activity --- OpenPGP-Keychain/src/main/AndroidManifest.xml | 9 + .../keychain/pgp/PgpConversionHelper.java | 24 ++ .../keychain/pgp/PgpKeyHelper.java | 5 +- .../keychain/provider/KeychainProvider.java | 19 +- .../keychain/ui/ViewCertActivity.java | 161 ++++++++++++++ .../keychain/ui/ViewKeyCertsFragment.java | 21 +- .../keychain/ui/ViewKeyMainFragment.java | 3 +- .../main/res/layout/view_cert_activity.xml | 207 ++++++++++++++++++ .../src/main/res/values/strings.xml | 9 + 9 files changed, 448 insertions(+), 10 deletions(-) create mode 100644 OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java create mode 100644 OpenPGP-Keychain/src/main/res/layout/view_cert_activity.xml diff --git a/OpenPGP-Keychain/src/main/AndroidManifest.xml b/OpenPGP-Keychain/src/main/AndroidManifest.xml index 4b68379e1..250d3edbe 100644 --- a/OpenPGP-Keychain/src/main/AndroidManifest.xml +++ b/OpenPGP-Keychain/src/main/AndroidManifest.xml @@ -92,6 +92,15 @@ android:name="android.support.PARENT_ACTIVITY" android:value=".ui.KeyListActivity" /> + + + to byte[] * diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java index b3e21685e..38480a766 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java @@ -438,7 +438,10 @@ public class PgpKeyHelper { break; } } - return algorithmStr + ", " + keySize + " bit"; + if(keySize > 0) + return algorithmStr + ", " + keySize + " bit"; + else + return algorithmStr; } /** diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java index 765bd1c44..171aa9912 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -558,6 +558,7 @@ public class KeychainProvider extends ContentProvider { return c; } + boolean all = false; switch (match) { case PUBLIC_KEY_RING: case SECRET_KEY_RING: @@ -719,8 +720,10 @@ public class KeychainProvider extends ContentProvider { break; - case CERTS_BY_KEY_ROW_ID: + case CERTS_BY_ROW_ID: case CERTS_BY_KEY_ROW_ID_ALL: + all = true; + case CERTS_BY_KEY_ROW_ID: qb.setTables(Tables.CERTS + " JOIN " + Tables.USER_IDS + " ON (" + Tables.CERTS + "." + Certs.KEY_RING_ROW_ID + " = " @@ -729,7 +732,7 @@ public class KeychainProvider extends ContentProvider { + Tables.CERTS + "." + Certs.RANK + " = " + Tables.USER_IDS + "." + UserIds.RANK // noooooooot sure about this~ database design - + ")" + (match == CERTS_BY_KEY_ROW_ID_ALL ? " LEFT" : "") + + ")" + (all ? " LEFT" : "") + " JOIN " + Tables.KEYS + " ON (" + Tables.CERTS + "." + Certs.KEY_ID_CERTIFIER + " = " + Tables.KEYS + "." + Keys.KEY_ID @@ -745,8 +748,11 @@ public class KeychainProvider extends ContentProvider { HashMap pmap2 = new HashMap(); pmap2.put(Certs._ID, Tables.CERTS + "." + Certs._ID); + pmap2.put(Certs.KEY_ID, Tables.CERTS + "." + Certs.KEY_ID); pmap2.put(Certs.RANK, Tables.CERTS + "." + Certs.RANK); + pmap2.put(Certs.CREATION, Tables.CERTS + "." + Certs.CREATION); pmap2.put(Certs.KEY_ID_CERTIFIER, Tables.CERTS + "." + Certs.KEY_ID_CERTIFIER); + pmap2.put(Certs.KEY_DATA, Tables.CERTS + "." + Certs.KEY_DATA); pmap2.put(Certs.VERIFIED, Tables.CERTS + "." + Certs.VERIFIED); // verified key data pmap2.put(UserIds.USER_ID, Tables.USER_IDS + "." + UserIds.USER_ID); @@ -754,8 +760,13 @@ public class KeychainProvider extends ContentProvider { pmap2.put("signer_uid", "signer." + UserIds.USER_ID + " AS signer_uid"); qb.setProjectionMap(pmap2); - qb.appendWhere(Tables.CERTS + "." + Certs.KEY_RING_ROW_ID + " = "); - qb.appendWhereEscapeString(uri.getPathSegments().get(2)); + if(match == CERTS_BY_ROW_ID) { + qb.appendWhere(Tables.CERTS + "." + Certs._ID + " = "); + qb.appendWhereEscapeString(uri.getPathSegments().get(1)); + } else { + qb.appendWhere(Tables.CERTS + "." + Certs.KEY_RING_ROW_ID + " = "); + qb.appendWhereEscapeString(uri.getPathSegments().get(2)); + } break; diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java new file mode 100644 index 000000000..2d0ee7f5d --- /dev/null +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java @@ -0,0 +1,161 @@ +/* + * 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; + +import android.content.Context; +import android.database.Cursor; +import android.database.DatabaseUtils; +import android.net.Uri; +import android.os.Bundle; +import android.support.v4.app.LoaderManager; +import android.support.v4.content.CursorLoader; +import android.support.v4.content.Loader; +import android.support.v7.app.ActionBar; +import android.support.v7.app.ActionBarActivity; +import android.text.format.DateFormat; +import android.widget.TextView; + +import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.pgp.PgpConversionHelper; +import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; +import org.sufficientlysecure.keychain.provider.KeychainContract; +import org.sufficientlysecure.keychain.util.Log; + +import java.util.Date; + +/** + * Swag + */ +public class ViewCertActivity extends ActionBarActivity + implements LoaderManager.LoaderCallbacks { + + // These are the rows that we will retrieve. + static final String[] PROJECTION = new String[] { + KeychainContract.Certs._ID, + KeychainContract.Certs.KEY_ID, + KeychainContract.UserIds.USER_ID, + KeychainContract.Certs.RANK, + KeychainContract.Certs.CREATION, + KeychainContract.Certs.KEY_ID_CERTIFIER, + "signer_uid", + KeychainContract.Certs.KEY_DATA + }; + + private Uri mDataUri; + + private TextView mSigneeKey, mSigneeUid, mRank, mAlgorithm, mType, mCreation, mExpiry; + private TextView mSignerKey, mSignerUid; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + ActionBar actionBar = getSupportActionBar(); + actionBar.setDisplayHomeAsUpEnabled(true); + + setContentView(R.layout.view_cert_activity); + + mSigneeKey = (TextView) findViewById(R.id.signee_key); + mSigneeUid = (TextView) findViewById(R.id.signee_uid); + mRank = (TextView) findViewById(R.id.subkey_rank); + mAlgorithm = (TextView) findViewById(R.id.algorithm); + mType = (TextView) findViewById(R.id.signature_type); + mCreation = (TextView) findViewById(R.id.creation); + mExpiry = (TextView) findViewById(R.id.expiry); + + mSignerKey = (TextView) findViewById(R.id.signer_key_id); + mSignerUid = (TextView) findViewById(R.id.signer_uid); + + mDataUri = getIntent().getData(); + if (mDataUri == null) { + Log.e(Constants.TAG, "Intent data missing. Should be Uri of key!"); + finish(); + return; + } + + getSupportLoaderManager().initLoader(0, null, this); + + } + + @Override + public Loader onCreateLoader(int id, Bundle args) { + // Now create and return a CursorLoader that will take care of + // creating a Cursor for the data being displayed. + return new CursorLoader(this, mDataUri, PROJECTION, null, null, null); + } + + @Override + public void onLoadFinished(Loader loader, Cursor data) { + if(data.moveToFirst()) { + String signeeKey = "0x" + PgpKeyHelper.convertKeyIdToHex(data.getLong(1)); + mSigneeKey.setText(signeeKey); + + String signeeUid = data.getString(2); + mSigneeUid.setText(signeeUid); + + String subkey_rank = Integer.toString(data.getInt(3)); + mRank.setText(subkey_rank); + + Date creationDate = new Date(data.getLong(4) * 1000); + mCreation.setText(DateFormat.getDateFormat(getApplicationContext()).format(creationDate)); + + String signerKey = "0x" + PgpKeyHelper.convertKeyIdToHex(data.getLong(5)); + mSignerKey.setText(signerKey); + + String signerUid = data.getString(6); + if(signerUid != null) + mSignerUid.setText(signerUid); + else + mSignerUid.setText(R.string.unknown_uid); + + byte[] sigData = data.getBlob(7); + PGPSignature sig = PgpConversionHelper.BytesToPGPSignature(sigData); + if(sig != null) { + String algorithmStr = PgpKeyHelper.getAlgorithmInfo(sig.getKeyAlgorithm(), 0); + mAlgorithm.setText(algorithmStr); + + switch(sig.getSignatureType()) { + case PGPSignature.DEFAULT_CERTIFICATION: + mType.setText(R.string.sig_type_default); break; + case PGPSignature.NO_CERTIFICATION: + mType.setText(R.string.sig_type_none); break; + case PGPSignature.CASUAL_CERTIFICATION: + mType.setText(R.string.sig_type_casual); break; + case PGPSignature.POSITIVE_CERTIFICATION: + mType.setText(R.string.sig_type_positive); break; + } + + long expiry = sig.getHashedSubPackets().getSignatureExpirationTime(); + if(expiry == 0) + mExpiry.setText("never"); + else { + Date expiryDate = new Date(creationDate.getTime() + expiry * 1000); + mExpiry.setText(DateFormat.getDateFormat(getApplicationContext()).format(expiryDate)); + } + } + } + } + + @Override + public void onLoaderReset(Loader loader) { + } + +} diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java index 3158ca713..4551bd4e0 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java @@ -17,8 +17,8 @@ package org.sufficientlysecure.keychain.ui; -import android.annotation.SuppressLint; import android.content.Context; +import android.content.Intent; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; @@ -30,6 +30,7 @@ import android.support.v4.widget.CursorAdapter; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.AdapterView; import android.widget.CheckBox; import android.widget.CompoundButton; import android.widget.TextView; @@ -47,10 +48,10 @@ import se.emilsjolander.stickylistheaders.StickyListHeadersListView; public class ViewKeyCertsFragment extends Fragment - implements LoaderManager.LoaderCallbacks { + implements LoaderManager.LoaderCallbacks, AdapterView.OnItemClickListener { // These are the rows that we will retrieve. - static final String[] PROJECTION = new String[]{ + static final String[] PROJECTION = new String[] { KeychainContract.Certs._ID, KeychainContract.Certs.VERIFIED, KeychainContract.Certs.RANK, @@ -116,6 +117,7 @@ public class ViewKeyCertsFragment extends Fragment mStickyList.setAreHeadersSticky(true); mStickyList.setDrawingListUnderStickyHeader(false); mStickyList.setFastScrollEnabled(true); + mStickyList.setOnItemClickListener(this); try { mStickyList.setFastScrollAlwaysVisible(true); @@ -153,6 +155,17 @@ public class ViewKeyCertsFragment extends Fragment mStickyList.setAdapter(mAdapter); } + /** + * On click on item, start key view activity + */ + @Override + public void onItemClick(AdapterView adapterView, View view, int position, long id) { + Intent viewIntent = null; + viewIntent = new Intent(getActivity(), ViewCertActivity.class); + viewIntent.setData(KeychainContract.Certs.buildCertsUri(Long.toString(id))); + startActivity(viewIntent); + } + @Override public void onLoaderReset(Loader loader) { // This is called when the last Cursor provided to onLoadFinished() @@ -166,6 +179,7 @@ public class ViewKeyCertsFragment extends Fragment */ private class CertListAdapter extends CursorAdapter implements StickyListHeadersAdapter { private LayoutInflater mInflater; + private int mIndexCertId; private int mIndexUserId, mIndexRank; private int mIndexSignerKeyId, mIndexSignerUserId; private int mIndexVerified; @@ -193,6 +207,7 @@ public class ViewKeyCertsFragment extends Fragment private void initIndex(Cursor cursor) { if (cursor != null) { + mIndexCertId = cursor.getColumnIndexOrThrow(KeychainContract.Certs._ID); mIndexUserId = cursor.getColumnIndexOrThrow(KeychainContract.UserIds.USER_ID); mIndexRank = cursor.getColumnIndexOrThrow(KeychainContract.UserIds.RANK); mIndexVerified = cursor.getColumnIndexOrThrow(KeychainContract.Certs.VERIFIED); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java index 6a87beb31..26c39b91d 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java @@ -48,7 +48,7 @@ import org.sufficientlysecure.keychain.util.Log; import java.util.Date; -public class ViewKeyMainFragment extends Fragment implements +public class ViewKeyMainFragment extends Fragment implements LoaderManager.LoaderCallbacks{ public static final String ARG_DATA_URI = "uri"; @@ -322,7 +322,6 @@ public class ViewKeyMainFragment extends Fragment implements } } - private void encryptToContact(Uri dataUri) { long keyId = ProviderHelper.getMasterKeyId(getActivity(), dataUri); diff --git a/OpenPGP-Keychain/src/main/res/layout/view_cert_activity.xml b/OpenPGP-Keychain/src/main/res/layout/view_cert_activity.xml new file mode 100644 index 000000000..c21362beb --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/layout/view_cert_activity.xml @@ -0,0 +1,207 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OpenPGP-Keychain/src/main/res/values/strings.xml b/OpenPGP-Keychain/src/main/res/values/strings.xml index 42f74fda2..ab81bee7a 100644 --- a/OpenPGP-Keychain/src/main/res/values/strings.xml +++ b/OpenPGP-Keychain/src/main/res/values/strings.xml @@ -443,5 +443,14 @@ available unavailable Show unknown signatures + Signer + Certificate Details + default + none + casual + positive + User ID + Subkey Rank + ]]> From 20193e099207b9de88c38fc7fe1a1640426ebdee Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Wed, 12 Mar 2014 02:30:49 +0100 Subject: [PATCH 11/30] add menu to viewcertactivity --- .../keychain/ui/ViewCertActivity.java | 46 +++++++++++++++++-- .../src/main/res/menu/view_cert.xml | 13 ++++++ 2 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 OpenPGP-Keychain/src/main/res/menu/view_cert.xml diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java index 2d0ee7f5d..c0ecc5001 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java @@ -17,10 +17,10 @@ package org.sufficientlysecure.keychain.ui; -import android.content.Context; +import android.content.Intent; import android.database.Cursor; -import android.database.DatabaseUtils; import android.net.Uri; +import android.os.Build; import android.os.Bundle; import android.support.v4.app.LoaderManager; import android.support.v4.content.CursorLoader; @@ -28,10 +28,11 @@ import android.support.v4.content.Loader; import android.support.v7.app.ActionBar; import android.support.v7.app.ActionBarActivity; import android.text.format.DateFormat; +import android.view.Menu; +import android.view.MenuItem; import android.widget.TextView; import org.spongycastle.openpgp.PGPSignature; -import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.pgp.PgpConversionHelper; @@ -61,6 +62,8 @@ public class ViewCertActivity extends ActionBarActivity private Uri mDataUri; + private long mSignerKeyId; + private TextView mSigneeKey, mSigneeUid, mRank, mAlgorithm, mType, mCreation, mExpiry; private TextView mSignerKey, mSignerUid; @@ -117,7 +120,8 @@ public class ViewCertActivity extends ActionBarActivity Date creationDate = new Date(data.getLong(4) * 1000); mCreation.setText(DateFormat.getDateFormat(getApplicationContext()).format(creationDate)); - String signerKey = "0x" + PgpKeyHelper.convertKeyIdToHex(data.getLong(5)); + mSignerKeyId = data.getLong(5); + String signerKey = "0x" + PgpKeyHelper.convertKeyIdToHex(mSignerKeyId); mSignerKey.setText(signerKey); String signerUid = data.getString(6); @@ -158,4 +162,38 @@ public class ViewCertActivity extends ActionBarActivity public void onLoaderReset(Loader loader) { } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + getMenuInflater().inflate(R.menu.view_cert, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.menu_view_cert_verify: + // TODO verification routine + return true; + case R.id.menu_view_cert_view_signer: + // can't do this before the data is initialized + // TODO notify user of this, maybe offer download? + if(mSignerKeyId == 0) + return true; + Intent viewIntent = null; + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { + viewIntent = new Intent(this, ViewKeyActivity.class); + } else { + viewIntent = new Intent(this, ViewKeyActivityJB.class); + } + viewIntent.setData(KeychainContract.KeyRings.buildPublicKeyRingsByMasterKeyIdUri( + Long.toString(mSignerKeyId)) + ); + startActivity(viewIntent); + return true; + } + return super.onOptionsItemSelected(item); + } + } diff --git a/OpenPGP-Keychain/src/main/res/menu/view_cert.xml b/OpenPGP-Keychain/src/main/res/menu/view_cert.xml new file mode 100644 index 000000000..dbdb2bfbe --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/menu/view_cert.xml @@ -0,0 +1,13 @@ + +

+ + + + \ No newline at end of file From 3d8d22c9ed67a6c7107764803f2baa31ed695edc Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Wed, 12 Mar 2014 02:47:19 +0100 Subject: [PATCH 12/30] add empty view to viewkeycertsfragment --- .../keychain/ui/ViewKeyCertsFragment.java | 2 ++ .../src/main/res/layout/view_key_certs_fragment.xml | 10 ++++++++++ OpenPGP-Keychain/src/main/res/values/strings.xml | 1 + 3 files changed, 13 insertions(+) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java index 4551bd4e0..e12c93d2f 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java @@ -124,6 +124,8 @@ public class ViewKeyCertsFragment extends Fragment } catch (ApiLevelTooLowException e) { } + mStickyList.setEmptyView(getActivity().findViewById(R.id.empty)); + // TODO this view is made visible if no data is available // mStickyList.setEmptyView(getActivity().findViewById(R.id.empty)); diff --git a/OpenPGP-Keychain/src/main/res/layout/view_key_certs_fragment.xml b/OpenPGP-Keychain/src/main/res/layout/view_key_certs_fragment.xml index 40db5e766..60f071bd4 100644 --- a/OpenPGP-Keychain/src/main/res/layout/view_key_certs_fragment.xml +++ b/OpenPGP-Keychain/src/main/res/layout/view_key_certs_fragment.xml @@ -33,6 +33,16 @@ android:layout_alignParentRight="true" android:layout_marginRight="16dp" /> + + \ No newline at end of file diff --git a/OpenPGP-Keychain/src/main/res/values/strings.xml b/OpenPGP-Keychain/src/main/res/values/strings.xml index ab81bee7a..723661928 100644 --- a/OpenPGP-Keychain/src/main/res/values/strings.xml +++ b/OpenPGP-Keychain/src/main/res/values/strings.xml @@ -452,5 +452,6 @@ User ID Subkey Rank ]]> + No certificates for this key From edb98c67f4031b8c3c1d43b49bba733171119be2 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Wed, 12 Mar 2014 02:58:42 +0100 Subject: [PATCH 13/30] add certify action button to viewkeymainfragment --- .../keychain/ui/ViewKeyMainFragment.java | 17 +++++++++++++++++ .../main/res/layout/view_key_main_fragment.xml | 11 +++++++++++ 2 files changed, 28 insertions(+) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java index 26c39b91d..00ca0d1f4 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java @@ -64,6 +64,7 @@ public class ViewKeyMainFragment extends Fragment implements private TextView mSecretKey; private BootstrapButton mActionEdit; private BootstrapButton mActionEncrypt; + private BootstrapButton mActionCertify; private ListView mUserIds; private ListView mKeys; @@ -94,6 +95,7 @@ public class ViewKeyMainFragment extends Fragment implements mKeys = (ListView) view.findViewById(R.id.keys); mActionEdit = (BootstrapButton) view.findViewById(R.id.action_edit); mActionEncrypt = (BootstrapButton) view.findViewById(R.id.action_encrypt); + mActionCertify = (BootstrapButton) view.findViewById(R.id.action_certify); return view; } @@ -130,6 +132,9 @@ public class ViewKeyMainFragment extends Fragment implements mSecretKey.setTextColor(getResources().getColor(R.color.emphasis)); mSecretKey.setText(R.string.secret_key_yes); + // certify button + mActionCertify.setVisibility(View.GONE); + // edit button mActionEdit.setVisibility(View.VISIBLE); mActionEdit.setOnClickListener(new View.OnClickListener() { @@ -143,6 +148,18 @@ public class ViewKeyMainFragment extends Fragment implements } else { mSecretKey.setTextColor(Color.BLACK); mSecretKey.setText(getResources().getString(R.string.secret_key_no)); + + // certify button + mActionCertify.setVisibility(View.VISIBLE); + mActionCertify.setOnClickListener(new View.OnClickListener() { + public void onClick(View view) { + certifyKey(KeychainContract.KeyRings.buildPublicKeyRingsByMasterKeyIdUri( + Long.toString(masterKeyId) + )); + } + }); + + // edit button mActionEdit.setVisibility(View.GONE); } } diff --git a/OpenPGP-Keychain/src/main/res/layout/view_key_main_fragment.xml b/OpenPGP-Keychain/src/main/res/layout/view_key_main_fragment.xml index fe5b417fb..644548183 100644 --- a/OpenPGP-Keychain/src/main/res/layout/view_key_main_fragment.xml +++ b/OpenPGP-Keychain/src/main/res/layout/view_key_main_fragment.xml @@ -251,6 +251,17 @@ bootstrapbutton:bb_icon_left="fa-lock" bootstrapbutton:bb_type="info" /> + + + \ No newline at end of file From 8fbd77fb15fda5d8984d6e39c1ebbce9696b0ae5 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sat, 15 Mar 2014 18:15:39 +0100 Subject: [PATCH 14/30] certify: preserve user id certificates when saving secret keys --- .../keychain/pgp/PgpKeyOperation.java | 64 +++++++++++++++++-- .../service/KeychainIntentService.java | 5 +- .../src/main/res/values/strings.xml | 1 + 3 files changed, 64 insertions(+), 6 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java index 78e48b4ab..5e7eb0a4a 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java @@ -36,6 +36,7 @@ import org.sufficientlysecure.keychain.Id; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Primes; import org.sufficientlysecure.keychain.util.ProgressDialogUpdater; @@ -46,6 +47,8 @@ import java.security.*; import java.util.ArrayList; import java.util.Date; import java.util.GregorianCalendar; +import java.util.Iterator; +import java.util.List; import java.util.TimeZone; public class PgpKeyOperation { @@ -198,7 +201,7 @@ public class PgpKeyOperation { public void buildSecretKey(ArrayList userIds, ArrayList keys, ArrayList keysUsages, ArrayList keysExpiryDates, - long masterKeyId, String oldPassPhrase, + PGPPublicKey oldPublicKey, String oldPassPhrase, String newPassPhrase) throws PgpGeneralException, NoSuchProviderException, PGPException, NoSuchAlgorithmException, SignatureException, IOException { @@ -249,9 +252,32 @@ public class PgpKeyOperation { updateProgress(R.string.progress_certifying_master_key, 20, 100); - // TODO: if we are editing a key, keep old certs, don't remake certs we don't have to. - + // re-add old certificates, or create new ones for new uids for (String userId : userIds) { + // re-add certs for this uid, take a note if self-signed cert is in there + boolean foundSelfSign = false; + Iterator it = tmpKey.getSignaturesForID(userId); + if(it != null) for(PGPSignature sig : new IterableIterator(it)) { + if(sig.getKeyID() == masterPublicKey.getKeyID()) { + // already have a self sign? skip this other one, then. + // note: PGPKeyRingGenerator adds one cert for the main user id, which + // will lead to duplicates. unfortunately, if we add any other here + // first, that will change the main user id order... + if(foundSelfSign) + continue; + foundSelfSign = true; + } + Log.d(Constants.TAG, "adding old sig for " + userId + " from " + + PgpKeyHelper.convertKeyIdToHex(sig.getKeyID())); + masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, sig); + } + + // there was an old self-signed certificate for this uid + if(foundSelfSign) + continue; + + Log.d(Constants.TAG, "generating self-signed cert for " + userId); + PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( masterPublicKey.getAlgorithm(), HashAlgorithmTags.SHA1) .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); @@ -330,7 +356,7 @@ public class PgpKeyOperation { updateProgress(R.string.progress_adding_sub_keys, 40, 100); for (int i = 1; i < keys.size(); ++i) { - updateProgress(40 + 50 * (i - 1) / (keys.size() - 1), 100); + updateProgress(40 + 40 * (i - 1) / (keys.size() - 1), 100); PGPSecretKey subKey = keys.get(i); PGPPublicKey subPublicKey = subKey.getPublicKey(); @@ -400,8 +426,38 @@ public class PgpKeyOperation { PGPSecretKeyRing secretKeyRing = keyGen.generateSecretKeyRing(); PGPPublicKeyRing publicKeyRing = keyGen.generatePublicKeyRing(); + updateProgress(R.string.progress_re_adding_certs, 80, 100); + + // re-add certificates from old public key + // TODO: this only takes care of user id certificates, what about others? + PGPPublicKey pubkey = publicKeyRing.getPublicKey(); + for(String uid : new IterableIterator(pubkey.getUserIDs())) { + for(PGPSignature sig : new IterableIterator(oldPublicKey.getSignaturesForID(uid))) { + // but skip self certificates + if(sig.getKeyID() == pubkey.getKeyID()) + continue; + pubkey = PGPPublicKey.addCertification(pubkey, uid, sig); + } + } + publicKeyRing = PGPPublicKeyRing.insertPublicKey(publicKeyRing, pubkey); + updateProgress(R.string.progress_saving_key_ring, 90, 100); + /* additional handy debug info + Log.d(Constants.TAG, " ------- in private key -------"); + 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 -------"); + 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); + } + } + */ + ProviderHelper.saveKeyRing(mContext, secretKeyRing); ProviderHelper.saveKeyRing(mContext, publicKeyRing); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java index 506ff2735..daaff5d54 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -543,8 +543,9 @@ public class KeychainIntentService extends IntentService ProviderHelper.getPGPSecretKeyRingByKeyId(this, masterKeyId), oldPassPhrase, newPassPhrase); } else { - keyOperations.buildSecretKey(userIds, keys, keysUsages, keysExpiryDates, masterKeyId, - oldPassPhrase, newPassPhrase); + PGPPublicKey pubkey = ProviderHelper.getPGPPublicKeyByKeyId(this, masterKeyId); + keyOperations.buildSecretKey(userIds, keys, keysUsages, keysExpiryDates, + pubkey, oldPassPhrase, newPassPhrase); } PassphraseCacheService.addCachedPassphrase(this, masterKeyId, newPassPhrase); diff --git a/OpenPGP-Keychain/src/main/res/values/strings.xml b/OpenPGP-Keychain/src/main/res/values/strings.xml index 0520a414b..265bcc6db 100644 --- a/OpenPGP-Keychain/src/main/res/values/strings.xml +++ b/OpenPGP-Keychain/src/main/res/values/strings.xml @@ -466,5 +466,6 @@ available unavailable User IDs to sign + Reapplying certificates From 5c28da44d6e4dfa72e69fecbacce6f988d5f6eb3 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sat, 15 Mar 2014 19:57:22 +0100 Subject: [PATCH 15/30] certs: provider cleanup and bugfixing --- .../keychain/provider/KeychainProvider.java | 45 ++++++++++--------- .../keychain/provider/ProviderHelper.java | 41 +++++++++-------- .../keychain/ui/CertifyKeyActivity.java | 8 ++-- .../keychain/ui/ViewCertActivity.java | 23 +++++----- .../keychain/ui/ViewKeyMainFragment.java | 24 +++++----- .../main/res/layout/view_cert_activity.xml | 18 -------- 6 files changed, 75 insertions(+), 84 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java index eff011bdf..7ee30a304 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -451,6 +451,9 @@ public class KeychainProvider extends ContentProvider { projectionMap.put(KeyRingsColumns.MASTER_KEY_ID, Tables.KEY_RINGS + "." + KeyRingsColumns.MASTER_KEY_ID); + // this is the count of known secret keys who certified this uid + projectionMap.put("verified", "COUNT(" + Tables.KEYS + "." + Keys._ID + ") AS verified"); + return projectionMap; } @@ -659,38 +662,37 @@ public class KeychainProvider extends ContentProvider { break; case PUBLIC_KEY_RING_BY_MASTER_KEY_ID_USER_ID: - qb.setTables(Tables.USER_IDS + " INNER JOIN " + Tables.KEY_RINGS + " ON " + "(" - + Tables.KEY_RINGS + "." + BaseColumns._ID + " = " + Tables.USER_IDS + "." - + KeysColumns.KEY_RING_ROW_ID + " )"); - qb.appendWhere(Tables.KEY_RINGS + "." + KeyRingsColumns.MASTER_KEY_ID + " = "); - qb.appendWhereEscapeString(uri.getPathSegments().get(3)); - - qb.setProjectionMap(getProjectionMapForUserIds()); - - break; - case PUBLIC_KEY_RING_USER_ID: case SECRET_KEY_RING_USER_ID: qb.setTables(Tables.USER_IDS - + " LEFT JOIN " + Tables.CERTS - + " ON (" + + " INNER JOIN " + Tables.KEY_RINGS + " ON (" + + Tables.KEY_RINGS + "." + BaseColumns._ID + " = " + + Tables.USER_IDS + "." + KeysColumns.KEY_RING_ROW_ID + + ") LEFT JOIN " + Tables.CERTS + " ON (" + Tables.USER_IDS + "." + UserIds.KEY_RING_ROW_ID + " = " + Tables.CERTS + "." + Certs.KEY_RING_ROW_ID + " AND " + Tables.USER_IDS + "." + UserIds.RANK + " = " + Tables.CERTS + "." + Certs.RANK + + ") LEFT JOIN " + Tables.KEYS + " ON (" + + Tables.KEYS + "." + Keys.KEY_ID + " = " + + Tables.CERTS + "." + Certs.KEY_ID_CERTIFIER + // might introduce a "trusted" flag later? for now, we simply assume + // every private key's signature is good. + + " AND " + Tables.KEYS + "." + Keys.TYPE + + " == " + KeyTypes.SECRET + ")"); groupBy = Tables.USER_IDS + "." + UserIds.RANK; - HashMap pmap = new HashMap(); - pmap.put(UserIds._ID, Tables.USER_IDS + "." + UserIds._ID); - pmap.put(UserIds.USER_ID, Tables.USER_IDS + "." + UserIds.USER_ID); - pmap.put(UserIds.RANK, Tables.USER_IDS + "." + UserIds.RANK); - pmap.put("verified", "COUNT(" + Tables.CERTS + "." + Certs._ID + ") AS verified"); - qb.setProjectionMap(pmap); + qb.setProjectionMap(getProjectionMapForUserIds()); - qb.appendWhere(Tables.USER_IDS + "." + UserIdsColumns.KEY_RING_ROW_ID + " = "); - qb.appendWhereEscapeString(uri.getPathSegments().get(2)); + if(match == PUBLIC_KEY_RING_BY_MASTER_KEY_ID_USER_ID) { + qb.appendWhere(Tables.KEY_RINGS + "." + KeyRingsColumns.MASTER_KEY_ID + " = "); + qb.appendWhereEscapeString(uri.getPathSegments().get(3)); + } else { + qb.appendWhere(Tables.USER_IDS + "." + UserIdsColumns.KEY_RING_ROW_ID + " = "); + qb.appendWhereEscapeString(uri.getPathSegments().get(2)); + } break; @@ -728,7 +730,8 @@ public class KeychainProvider extends ContentProvider { + "signer." + Keys.RANK + " = 0" + ")"); - // groupBy = Tables.USER_IDS + "." + UserIds.RANK; + groupBy = Tables.CERTS + "." + Certs.RANK + ", " + + Tables.CERTS + "." + Certs.KEY_ID_CERTIFIER; HashMap pmap2 = new HashMap(); pmap2.put(Certs._ID, Tables.CERTS + "." + Certs._ID); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index dfc65f778..3d81743f1 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -207,7 +207,7 @@ public class ProviderHelper { } // get a list of owned secret keys, for verification filtering - Map allKeyRings = getPGPKeyRings(context, KeyRings.buildPublicKeyRingsUri()); + Map allKeyRings = getPGPKeyRings(context, KeyRings.buildSecretKeyRingsUri()); int userIdRank = 0; for (String userId : new IterableIterator(masterKey.getUserIDs())) { @@ -218,35 +218,34 @@ public class ProviderHelper { masterKey.getSignaturesForID(userId))) { long certId = cert.getKeyID(); boolean verified = false; - // only care for signatures from our own private keys + // do verify signatures from our own private keys if(allKeyRings.containsKey(certId)) try { // mark them as verified - cert.init(new JcaPGPContentVerifierBuilderProvider().setProvider("SC"), allKeyRings.get(certId).getPublicKey()); + cert.init( + new JcaPGPContentVerifierBuilderProvider().setProvider( + Constants.BOUNCY_CASTLE_PROVIDER_NAME), + allKeyRings.get(certId).getPublicKey()); verified = cert.verifyCertification(userId, masterKey); - // TODO: at this point, we only save signatures from available secret keys. - // should we save all? those are quite a lot of rows for info we don't really - // use. I left it out for now - it is available from key servers, so we can - // always get it later. - Log.d(Constants.TAG, "sig for " + userId + " " + verified + " from " + Log.d(Constants.TAG, "Verified sig for " + userId + " " + verified + " from " + PgpKeyHelper.convertKeyIdToHex(cert.getKeyID()) ); - operations.add(buildPublicCertOperations( - context, keyRingRowId, userIdRank, masterKey.getKeyID(), cert, verified)); } catch(SignatureException e) { - Log.e(Constants.TAG, "Signature verification failed.", e); - } catch(PGPException e) { - Log.e(Constants.TAG, "Signature verification failed.", e); - } else { - Log.d(Constants.TAG, "ignored sig for " + Log.e(Constants.TAG, "Signature verification failed! " + PgpKeyHelper.convertKeyIdToHex(masterKey.getKeyID()) + " from " - + PgpKeyHelper.convertKeyIdToHex(cert.getKeyID()) - ); - operations.add(buildPublicCertOperations( - context, keyRingRowId, userIdRank, masterKey.getKeyID(), cert, false)); + + PgpKeyHelper.convertKeyIdToHex(cert.getKeyID()), e); + } catch(PGPException e) { + Log.e(Constants.TAG, "Signature verification failed! " + + PgpKeyHelper.convertKeyIdToHex(masterKey.getKeyID()) + + " from " + + PgpKeyHelper.convertKeyIdToHex(cert.getKeyID()), e); } - // if we wanted to save all, not just our own verifications - // buildPublicCertOperations(context, keyRingRowId, rank, cert, verified); + Log.d(Constants.TAG, "sig for " + userId + " from " + + PgpKeyHelper.convertKeyIdToHex(cert.getKeyID()) + ); + // regardless of verification, save the certification + operations.add(buildPublicCertOperations( + context, keyRingRowId, userIdRank, masterKey.getKeyID(), cert, verified)); } ++userIdRank; diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java index d8df5ced3..f2e6c4bd9 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java @@ -43,6 +43,7 @@ import org.sufficientlysecure.keychain.helper.Preferences; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.provider.KeychainContract; +import org.sufficientlysecure.keychain.provider.KeychainDatabase; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; @@ -164,7 +165,7 @@ public class CertifyKeyActivity extends ActionBarActivity implements KeychainContract.KeyRings._ID, KeychainContract.KeyRings.MASTER_KEY_ID, KeychainContract.Keys.FINGERPRINT, - KeychainContract.UserIds.USER_ID + KeychainContract.UserIds.USER_ID, }; static final int INDEX_MASTER_KEY_ID = 1; static final int INDEX_FINGERPRINT = 2; @@ -174,10 +175,11 @@ public class CertifyKeyActivity extends ActionBarActivity implements new String[]{ KeychainContract.UserIds._ID, KeychainContract.UserIds.USER_ID, - KeychainContract.UserIds.RANK + KeychainContract.UserIds.RANK, + "verified" }; static final String USER_IDS_SORT_ORDER = - KeychainContract.UserIds.RANK + " ASC"; + KeychainDatabase.Tables.USER_IDS + "." + KeychainContract.UserIds.RANK + " ASC"; @Override public Loader onCreateLoader(int id, Bundle args) { diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java index c0ecc5001..611864d8e 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java @@ -53,12 +53,17 @@ public class ViewCertActivity extends ActionBarActivity KeychainContract.Certs._ID, KeychainContract.Certs.KEY_ID, KeychainContract.UserIds.USER_ID, - KeychainContract.Certs.RANK, KeychainContract.Certs.CREATION, KeychainContract.Certs.KEY_ID_CERTIFIER, "signer_uid", KeychainContract.Certs.KEY_DATA }; + private static final int INDEX_KEY_ID = 1; + private static final int INDEX_USER_ID = 2; + private static final int INDEX_CREATION = 3; + private static final int INDEX_KEY_ID_CERTIFIER = 4; + private static final int INDEX_UID_CERTIFIER = 5; + private static final int INDEX_KEY_DATA = 6; private Uri mDataUri; @@ -78,7 +83,6 @@ public class ViewCertActivity extends ActionBarActivity mSigneeKey = (TextView) findViewById(R.id.signee_key); mSigneeUid = (TextView) findViewById(R.id.signee_uid); - mRank = (TextView) findViewById(R.id.subkey_rank); mAlgorithm = (TextView) findViewById(R.id.algorithm); mType = (TextView) findViewById(R.id.signature_type); mCreation = (TextView) findViewById(R.id.creation); @@ -108,29 +112,26 @@ public class ViewCertActivity extends ActionBarActivity @Override public void onLoadFinished(Loader loader, Cursor data) { if(data.moveToFirst()) { - String signeeKey = "0x" + PgpKeyHelper.convertKeyIdToHex(data.getLong(1)); + String signeeKey = "0x" + PgpKeyHelper.convertKeyIdToHex(data.getLong(INDEX_KEY_ID)); mSigneeKey.setText(signeeKey); - String signeeUid = data.getString(2); + String signeeUid = data.getString(INDEX_USER_ID); mSigneeUid.setText(signeeUid); - String subkey_rank = Integer.toString(data.getInt(3)); - mRank.setText(subkey_rank); - - Date creationDate = new Date(data.getLong(4) * 1000); + Date creationDate = new Date(data.getLong(INDEX_CREATION) * 1000); mCreation.setText(DateFormat.getDateFormat(getApplicationContext()).format(creationDate)); - mSignerKeyId = data.getLong(5); + mSignerKeyId = data.getLong(INDEX_KEY_ID_CERTIFIER); String signerKey = "0x" + PgpKeyHelper.convertKeyIdToHex(mSignerKeyId); mSignerKey.setText(signerKey); - String signerUid = data.getString(6); + String signerUid = data.getString(INDEX_UID_CERTIFIER); if(signerUid != null) mSignerUid.setText(signerUid); else mSignerUid.setText(R.string.unknown_uid); - byte[] sigData = data.getBlob(7); + byte[] sigData = data.getBlob(INDEX_KEY_DATA); PGPSignature sig = PgpConversionHelper.BytesToPGPSignature(sigData); if(sig != null) { String algorithmStr = PgpKeyHelper.getAlgorithmInfo(sig.getKeyAlgorithm(), 0); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java index 46c3e0b92..8f8c13c29 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java @@ -198,22 +198,26 @@ public class ViewKeyMainFragment extends Fragment implements static final int KEYRING_INDEX_MASTER_KEY_ID = 1; static final int KEYRING_INDEX_USER_ID = 2; - static final String[] USER_IDS_PROJECTION = - new String[]{ - KeychainContract.UserIds._ID, - KeychainContract.UserIds.USER_ID, - KeychainContract.UserIds.RANK, - }; + static final String[] USER_IDS_PROJECTION = new String[]{ + KeychainContract.UserIds._ID, + KeychainContract.UserIds.USER_ID, + KeychainContract.UserIds.RANK, + "verified", + }; + // not the main user id + static final String USER_IDS_SELECTION = + KeychainDatabase.Tables.USER_IDS + "." + KeychainContract.UserIds.RANK + " > 0 "; static final String USER_IDS_SORT_ORDER = - KeychainContract.UserIds.RANK + " COLLATE LOCALIZED ASC"; + KeychainDatabase.Tables.USER_IDS + "." + KeychainContract.UserIds.RANK + " COLLATE LOCALIZED ASC"; - static final String[] KEYS_PROJECTION = - new String[]{KeychainContract.Keys._ID, KeychainContract.Keys.KEY_ID, + static final String[] KEYS_PROJECTION = new String[]{ + KeychainContract.Keys._ID, KeychainContract.Keys.KEY_ID, KeychainContract.Keys.IS_MASTER_KEY, KeychainContract.Keys.ALGORITHM, KeychainContract.Keys.KEY_SIZE, KeychainContract.Keys.CAN_CERTIFY, KeychainContract.Keys.CAN_SIGN, KeychainContract.Keys.CAN_ENCRYPT, KeychainContract.Keys.CREATION, KeychainContract.Keys.EXPIRY, - KeychainContract.Keys.FINGERPRINT}; + KeychainContract.Keys.FINGERPRINT + }; static final String KEYS_SORT_ORDER = KeychainContract.Keys.RANK + " ASC"; static final int KEYS_INDEX_ID = 0; static final int KEYS_INDEX_KEY_ID = 1; diff --git a/OpenPGP-Keychain/src/main/res/layout/view_cert_activity.xml b/OpenPGP-Keychain/src/main/res/layout/view_cert_activity.xml index c21362beb..3c6c09429 100644 --- a/OpenPGP-Keychain/src/main/res/layout/view_cert_activity.xml +++ b/OpenPGP-Keychain/src/main/res/layout/view_cert_activity.xml @@ -60,24 +60,6 @@ android:paddingRight="5dip" /> - - - - - - - From f192c701794716d3b780b635342cf0f2e8b18ef5 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sat, 15 Mar 2014 19:58:17 +0100 Subject: [PATCH 16/30] certs: green dots for verified status \o/ --- .../keychain/ui/adapter/ViewKeyUserIdsAdapter.java | 12 ++++++++++-- .../src/main/res/layout/view_key_userids_item.xml | 12 +++++++++++- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java index 980c401da..4ae6131c9 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java @@ -25,6 +25,7 @@ import android.view.View; import android.view.ViewGroup; import android.widget.CheckBox; import android.widget.CompoundButton; +import android.widget.ImageView; import android.widget.TextView; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; @@ -90,18 +91,25 @@ public class ViewKeyUserIdsAdapter extends CursorAdapter { TextView vRank = (TextView) view.findViewById(R.id.rank); TextView vUserId = (TextView) view.findViewById(R.id.userId); TextView vAddress = (TextView) view.findViewById(R.id.address); + ImageView vVerified = (ImageView) view.findViewById(R.id.certified); vRank.setText(Integer.toString(cursor.getInt(mIndexRank))); String[] userId = PgpKeyHelper.splitUserId(cursor.getString(mIndexUserId)); - int verified = cursor.getInt(mVerifiedId); if (userId[0] != null) { - vUserId.setText(userId[0] + (verified > 0 ? " (ok)" : "(nope)")); + vUserId.setText(userId[0]); } else { vUserId.setText(R.string.user_id_no_name); } vAddress.setText(userId[1]); + int verified = cursor.getInt(mVerifiedId); + // TODO introduce own resource for this :) + if(verified > 0) + vVerified.setImageResource(android.R.drawable.presence_online); + else + vVerified.setImageResource(android.R.drawable.presence_invisible); + // don't care further if checkboxes aren't shown if(mCheckStates == null) return; diff --git a/OpenPGP-Keychain/src/main/res/layout/view_key_userids_item.xml b/OpenPGP-Keychain/src/main/res/layout/view_key_userids_item.xml index 2e8cfeb04..aa14e6d2f 100644 --- a/OpenPGP-Keychain/src/main/res/layout/view_key_userids_item.xml +++ b/OpenPGP-Keychain/src/main/res/layout/view_key_userids_item.xml @@ -24,7 +24,8 @@ + android:layout_height="wrap_content" + android:layout_weight="1"> + + + From e8ad4317a49cc86ddd5e147fe18f82beb02f9717 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Mon, 17 Mar 2014 14:10:40 +0100 Subject: [PATCH 17/30] certs: more filters, better initial uid selection, code cosmetics - add three types of filters for certificate list (ui looks like crap there, need to work on that) - select uncertified uids by default in CertifyKeyActivity - move some code around in KeychainProvider, 's slightly less hacky now --- .../keychain/provider/KeychainProvider.java | 49 ++++++++++++------- .../keychain/provider/ProviderHelper.java | 2 +- .../keychain/ui/ViewKeyCertsFragment.java | 48 ++++++++++++------ .../ui/adapter/ViewKeyUserIdsAdapter.java | 9 ++-- .../res/layout/view_key_certs_fragment.xml | 31 ++++++------ .../src/main/res/values/strings.xml | 3 ++ 6 files changed, 87 insertions(+), 55 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java index 7ee30a304..f34423a71 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -82,6 +82,7 @@ public class KeychainProvider extends ContentProvider { private static final int CERTS_BY_KEY_ROW_ID = 404; private static final int CERTS_BY_KEY_ROW_ID_ALL = 405; private static final int CERTS_BY_CERTIFIER_ID = 406; + private static final int CERTS_BY_KEY_ROW_ID_HAS_SECRET = 407; // private static final int DATA_STREAM = 401; @@ -256,6 +257,8 @@ public class KeychainProvider extends ContentProvider { + KeychainContract.PATH_BY_KEY_ROW_ID + "/#", CERTS_BY_KEY_ROW_ID); matcher.addURI(authority, KeychainContract.BASE_CERTS + "/" + KeychainContract.PATH_BY_KEY_ROW_ID + "/#/all", CERTS_BY_KEY_ROW_ID_ALL); + matcher.addURI(authority, KeychainContract.BASE_CERTS + "/" + + KeychainContract.PATH_BY_KEY_ROW_ID + "/#/has_secret", CERTS_BY_KEY_ROW_ID_HAS_SECRET); matcher.addURI(authority, KeychainContract.BASE_CERTS + "/" + KeychainContract.PATH_BY_KEY_ID + "/#", CERTS_BY_KEY_ID); matcher.addURI(authority, KeychainContract.BASE_CERTS + "/" @@ -456,6 +459,23 @@ public class KeychainProvider extends ContentProvider { return projectionMap; } + private HashMap getProjectionMapForCerts() { + + HashMap pmap = new HashMap(); + pmap.put(Certs._ID, Tables.CERTS + "." + Certs._ID); + pmap.put(Certs.KEY_ID, Tables.CERTS + "." + Certs.KEY_ID); + pmap.put(Certs.RANK, Tables.CERTS + "." + Certs.RANK); + pmap.put(Certs.CREATION, Tables.CERTS + "." + Certs.CREATION); + pmap.put(Certs.KEY_ID_CERTIFIER, Tables.CERTS + "." + Certs.KEY_ID_CERTIFIER); + pmap.put(Certs.KEY_DATA, Tables.CERTS + "." + Certs.KEY_DATA); + pmap.put(Certs.VERIFIED, Tables.CERTS + "." + Certs.VERIFIED); + // verified key data + pmap.put(UserIds.USER_ID, Tables.USER_IDS + "." + UserIds.USER_ID); + // verifying key data + pmap.put("signer_uid", "signer." + UserIds.USER_ID + " AS signer_uid"); + + return pmap; + } /** * Builds default query for keyRings: KeyRings table is joined with UserIds and Keys @@ -521,7 +541,6 @@ public class KeychainProvider extends ContentProvider { // all query() parameters, for good measure String groupBy = null, having = null; - boolean all = false; switch (match) { case UNIFIED_KEY_RING: @@ -709,7 +728,7 @@ public class KeychainProvider extends ContentProvider { case CERTS_BY_ROW_ID: case CERTS_BY_KEY_ROW_ID_ALL: - all = true; + case CERTS_BY_KEY_ROW_ID_HAS_SECRET: case CERTS_BY_KEY_ROW_ID: qb.setTables(Tables.CERTS + " JOIN " + Tables.USER_IDS + " ON (" @@ -719,10 +738,13 @@ public class KeychainProvider extends ContentProvider { + Tables.CERTS + "." + Certs.RANK + " = " + Tables.USER_IDS + "." + UserIds.RANK // noooooooot sure about this~ database design - + ")" + (all ? " LEFT" : "") + + ")" + (match == CERTS_BY_KEY_ROW_ID_ALL ? " LEFT" : "") + " JOIN " + Tables.KEYS + " ON (" + Tables.CERTS + "." + Certs.KEY_ID_CERTIFIER + " = " + Tables.KEYS + "." + Keys.KEY_ID + + (match == CERTS_BY_KEY_ROW_ID_HAS_SECRET ? + " AND " + Tables.KEYS + "." + Keys.TYPE + " = " + KeyTypes.SECRET : "" + ) + ") LEFT JOIN " + Tables.USER_IDS + " AS signer ON (" + Tables.KEYS + "." + Keys.KEY_RING_ROW_ID + " = " + "signer." + UserIds.KEY_RING_ROW_ID @@ -730,23 +752,11 @@ public class KeychainProvider extends ContentProvider { + "signer." + Keys.RANK + " = 0" + ")"); + qb.setProjectionMap(getProjectionMapForCerts()); + groupBy = Tables.CERTS + "." + Certs.RANK + ", " + Tables.CERTS + "." + Certs.KEY_ID_CERTIFIER; - HashMap pmap2 = new HashMap(); - pmap2.put(Certs._ID, Tables.CERTS + "." + Certs._ID); - pmap2.put(Certs.KEY_ID, Tables.CERTS + "." + Certs.KEY_ID); - pmap2.put(Certs.RANK, Tables.CERTS + "." + Certs.RANK); - pmap2.put(Certs.CREATION, Tables.CERTS + "." + Certs.CREATION); - pmap2.put(Certs.KEY_ID_CERTIFIER, Tables.CERTS + "." + Certs.KEY_ID_CERTIFIER); - pmap2.put(Certs.KEY_DATA, Tables.CERTS + "." + Certs.KEY_DATA); - pmap2.put(Certs.VERIFIED, Tables.CERTS + "." + Certs.VERIFIED); - // verified key data - pmap2.put(UserIds.USER_ID, Tables.USER_IDS + "." + UserIds.USER_ID); - // verifying key data - pmap2.put("signer_uid", "signer." + UserIds.USER_ID + " AS signer_uid"); - qb.setProjectionMap(pmap2); - if(match == CERTS_BY_ROW_ID) { qb.appendWhere(Tables.CERTS + "." + Certs._ID + " = "); qb.appendWhereEscapeString(uri.getPathSegments().get(1)); @@ -867,10 +877,11 @@ public class KeychainProvider extends ContentProvider { rowUri = ApiApps.buildIdUri(Long.toString(rowId)); break; - case CERTS_BY_ROW_ID: + case CERTS_BY_KEY_ROW_ID: rowId = db.insertOrThrow(Tables.CERTS, null, values); // kinda useless.. should this be buildCertsByKeyRowIdUri? - rowUri = Certs.buildCertsUri(Long.toString(rowId)); + // rowUri = Certs.buildCertsUri(Long.toString(rowId)); + rowUri = uri; break; default: diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index 3d81743f1..1d249c67e 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -374,7 +374,7 @@ public class ProviderHelper { values.put(Certs.VERIFIED, verified); values.put(Certs.KEY_DATA, cert.getEncoded()); - Uri uri = Certs.buildCertsUri(Long.toString(keyRingRowId)); + Uri uri = Certs.buildCertsByKeyRowIdUri(Long.toString(keyRingRowId)); return ContentProviderOperation.newInsert(uri).withValues(values).build(); } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java index e4c96b34b..c65e9e691 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java @@ -31,8 +31,8 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; -import android.widget.CheckBox; -import android.widget.CompoundButton; +import android.widget.ArrayAdapter; +import android.widget.Spinner; import android.widget.TextView; import org.sufficientlysecure.keychain.Constants; @@ -42,8 +42,6 @@ import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainDatabase; import org.sufficientlysecure.keychain.util.Log; -import com.beardedhen.androidbootstrap.BootstrapButton; - import se.emilsjolander.stickylistheaders.ApiLevelTooLowException; import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter; import se.emilsjolander.stickylistheaders.StickyListHeadersListView; @@ -64,14 +62,14 @@ public class ViewKeyCertsFragment extends Fragment // sort by our user id, static final String SORT_ORDER = - KeychainDatabase.Tables.USER_IDS + "." + KeychainContract.UserIds.USER_ID + " ASC, " + KeychainDatabase.Tables.USER_IDS + "." + KeychainContract.UserIds.RANK + " ASC, " + KeychainDatabase.Tables.CERTS + "." + KeychainContract.Certs.VERIFIED + " DESC, " + "signer_uid ASC"; public static final String ARG_KEYRING_ROW_ID = "row_id"; private StickyListHeadersListView mStickyList; - private CheckBox mShowUnknown; + private Spinner mSpinner; private CertListAdapter mAdapter; private boolean mUnknownShown = false; @@ -85,11 +83,18 @@ public class ViewKeyCertsFragment extends Fragment return view; } - private void toggleShowUnknown(boolean shown) { - if(shown) - mDataUri = mBaseUri.buildUpon().appendPath("all").build(); - else - mDataUri = mBaseUri; + private void changeShowState(int type) { + switch(type) { + case 0: + mDataUri = mBaseUri.buildUpon().appendPath("has_secret").build(); + break; + case 1: + mDataUri = mBaseUri; + break; + case 2: + mDataUri = mBaseUri.buildUpon().appendPath("all").build(); + break; + } getLoaderManager().restartLoader(0, null, this); } @@ -97,11 +102,24 @@ public class ViewKeyCertsFragment extends Fragment public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - mShowUnknown = (CheckBox) getActivity().findViewById(R.id.showUnknown); - mShowUnknown.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + mSpinner = (Spinner) getActivity().findViewById(R.id.spinner); + ArrayAdapter adapter = new ArrayAdapter(getActivity(), + android.R.layout.simple_spinner_item, new String[] { + getResources().getString(R.string.certs_list_known_secret), + getResources().getString(R.string.certs_list_known), + getResources().getString(R.string.certs_list_all) + } ); + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + mSpinner.setAdapter(adapter); + mSpinner.setSelection(1); + mSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override - public void onCheckedChanged(CompoundButton compoundButton, boolean b) { - toggleShowUnknown(b); + public void onItemSelected(AdapterView adapterView, View view, int i, long l) { + changeShowState(i); + } + + @Override + public void onNothingSelected(AdapterView adapterView) { } }); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java index 4ae6131c9..2778ed08c 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java @@ -63,8 +63,11 @@ public class ViewKeyUserIdsAdapter extends CursorAdapter { int count = newCursor.getCount(); mCheckStates.ensureCapacity(count); // initialize to true (use case knowledge: we usually want to sign all uids) - for(int i = 0; i < count; i++) - mCheckStates.add(true); + for(int i = 0; i < count; i++) { + newCursor.moveToPosition(i); + int verified = newCursor.getInt(mVerifiedId); + mCheckStates.add(verified == 0); + } } } @@ -116,7 +119,7 @@ public class ViewKeyUserIdsAdapter extends CursorAdapter { final CheckBox vCheckBox = (CheckBox) view.findViewById(R.id.checkBox); final int position = cursor.getPosition(); - vCheckBox.setClickable(false); + vCheckBox.setOnCheckedChangeListener(null); vCheckBox.setChecked(mCheckStates.get(position)); vCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override diff --git a/OpenPGP-Keychain/src/main/res/layout/view_key_certs_fragment.xml b/OpenPGP-Keychain/src/main/res/layout/view_key_certs_fragment.xml index 60f071bd4..b2bfe1700 100644 --- a/OpenPGP-Keychain/src/main/res/layout/view_key_certs_fragment.xml +++ b/OpenPGP-Keychain/src/main/res/layout/view_key_certs_fragment.xml @@ -9,29 +9,26 @@ android:layout_height="wrap_content" android:orientation="vertical"> + + - - + android:paddingLeft="16dp" + android:layout_alignParentStart="false" + android:layout_alignParentEnd="false" + android:layout_below="@+id/spinner" /> No certificates for this key User IDs to sign Reapplying certificates + Show by known secret keys + Show by known public keys + Show all certificates From 2802718efb3be3dd39743445de886707dead680f Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sat, 5 Apr 2014 21:38:03 +0200 Subject: [PATCH 18/30] certs: more fixes, almost working now --- .../keychain/provider/KeychainProvider.java | 34 ++++++++++------ .../keychain/provider/ProviderHelper.java | 3 ++ .../keychain/ui/ViewKeyCertsFragment.java | 40 +++++++++---------- .../keychain/ui/ViewKeyMainFragment.java | 3 +- .../ui/adapter/ViewKeyUserIdsAdapter.java | 10 +++-- 5 files changed, 52 insertions(+), 38 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java index 83b3dd744..01120eedf 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -369,19 +369,29 @@ public class KeychainProvider extends ContentProvider { case KEY_RING_USER_IDS: { HashMap projectionMap = new HashMap(); projectionMap.put(UserIds._ID, Tables.USER_IDS + ".oid AS _id"); - projectionMap.put(UserIds.MASTER_KEY_ID, UserIds.MASTER_KEY_ID); - projectionMap.put(UserIds.USER_ID, UserIds.USER_ID); - projectionMap.put(UserIds.RANK, UserIds.RANK); - projectionMap.put(UserIds.IS_PRIMARY, UserIds.IS_PRIMARY); - projectionMap.put(UserIds.VERIFIED, "0 AS " + UserIds.VERIFIED); + projectionMap.put(UserIds.MASTER_KEY_ID, Tables.USER_IDS + "." + UserIds.MASTER_KEY_ID); + projectionMap.put(UserIds.USER_ID, Tables.USER_IDS + "." + UserIds.USER_ID); + projectionMap.put(UserIds.RANK, Tables.USER_IDS + "." + UserIds.RANK); + projectionMap.put(UserIds.IS_PRIMARY, Tables.USER_IDS + "." + UserIds.IS_PRIMARY); + // we take the minimum (>0) here, where "1" is "verified by known secret key" + projectionMap.put(UserIds.VERIFIED, "MIN(" + Certs.VERIFIED + ") AS " + UserIds.VERIFIED); qb.setProjectionMap(projectionMap); - qb.setTables(Tables.USER_IDS); - qb.appendWhere(UserIds.MASTER_KEY_ID + " = "); + qb.setTables(Tables.USER_IDS + + " LEFT JOIN " + Tables.CERTS + " ON (" + + Tables.USER_IDS + "." + UserIds.MASTER_KEY_ID + " = " + + Tables.CERTS + "." + Certs.MASTER_KEY_ID + + " AND " + Tables.USER_IDS + "." + UserIds.RANK + " = " + + Tables.CERTS + "." + Certs.RANK + + " AND " + Tables.CERTS + "." + Certs.VERIFIED + " > 0" + + ")"); + groupBy = Tables.USER_IDS + "." + UserIds.RANK; + + qb.appendWhere(Tables.USER_IDS + "." + UserIds.MASTER_KEY_ID + " = "); qb.appendWhereEscapeString(uri.getPathSegments().get(1)); if (TextUtils.isEmpty(sortOrder)) { - sortOrder = UserIds.RANK + " ASC"; + sortOrder = Tables.USER_IDS + "." + UserIds.RANK + " ASC"; } break; @@ -433,9 +443,7 @@ public class KeychainProvider extends ContentProvider { projectionMap.put(Certs.KEY_ID_CERTIFIER, Tables.CERTS + "." + Certs.KEY_ID_CERTIFIER); projectionMap.put(Certs.VERIFIED, Tables.CERTS + "." + Certs.VERIFIED); projectionMap.put(Certs.KEY_DATA, Tables.CERTS + "." + Certs.KEY_DATA); - // verified key data projectionMap.put(Certs.USER_ID, Tables.USER_IDS + "." + UserIds.USER_ID); - // verifying key data projectionMap.put(Certs.SIGNER_UID, "signer." + UserIds.USER_ID + " AS " + Certs.SIGNER_UID); qb.setProjectionMap(projectionMap); @@ -447,7 +455,7 @@ public class KeychainProvider extends ContentProvider { + Tables.CERTS + "." + Certs.RANK + " = " + Tables.USER_IDS + "." + UserIds.RANK + ") LEFT JOIN " + Tables.USER_IDS + " AS signer ON (" - + Tables.CERTS + "." + Keys.MASTER_KEY_ID + " = " + + Tables.CERTS + "." + Certs.KEY_ID_CERTIFIER + " = " + "signer." + UserIds.MASTER_KEY_ID + " AND " + "signer." + Keys.RANK + " = 0" @@ -553,7 +561,9 @@ public class KeychainProvider extends ContentProvider { break; case KEY_RING_CERTS: - db.insertOrThrow(Tables.CERTS, null, values); + // we replace here, keeping only the latest signature + // TODO this would be better handled in saveKeyRing directly! + db.replaceOrThrow(Tables.CERTS, null, values); keyId = values.getAsLong(Certs.MASTER_KEY_ID); break; diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index 503fed3c9..02d8d29f2 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -223,6 +223,9 @@ public class ProviderHelper { // get a list of owned secret keys, for verification filtering Map allKeyRings = getPGPKeyRings(context, KeyRingData.buildSecretKeyRingUri()); + // special case: available secret keys verify themselves! + if(secretRing != null) + allKeyRings.put(secretRing.getSecretKey().getKeyID(), secretRing); int userIdRank = 0; for (String userId : new IterableIterator(masterKey.getUserIDs())) { diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java index 26e72f10b..1696d18dd 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java @@ -38,8 +38,8 @@ import android.widget.TextView; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; -import org.sufficientlysecure.keychain.provider.KeychainContract; -import org.sufficientlysecure.keychain.provider.KeychainDatabase; +import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; +import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables; import org.sufficientlysecure.keychain.util.Log; import se.emilsjolander.stickylistheaders.ApiLevelTooLowException; @@ -52,20 +52,20 @@ public class ViewKeyCertsFragment extends Fragment // These are the rows that we will retrieve. static final String[] PROJECTION = new String[] { - KeychainContract.Certs._ID, - KeychainContract.Certs.MASTER_KEY_ID, - KeychainContract.Certs.VERIFIED, - KeychainContract.Certs.RANK, - KeychainContract.Certs.KEY_ID_CERTIFIER, - KeychainContract.Certs.USER_ID, - KeychainContract.Certs.SIGNER_UID + Certs._ID, + Certs.MASTER_KEY_ID, + Certs.VERIFIED, + Certs.RANK, + Certs.KEY_ID_CERTIFIER, + Certs.USER_ID, + Certs.SIGNER_UID }; // sort by our user id, static final String SORT_ORDER = - KeychainDatabase.Tables.USER_IDS + "." + KeychainContract.UserIds.RANK + " ASC, " - + KeychainDatabase.Tables.CERTS + "." + KeychainContract.Certs.VERIFIED + " DESC, " - + KeychainContract.Certs.SIGNER_UID + " ASC"; + Tables.CERTS + "." + Certs.RANK + " ASC, " + + Certs.VERIFIED + " DESC, " + + Certs.SIGNER_UID + " ASC"; public static final String ARG_DATA_URI = "data_uri"; @@ -133,7 +133,7 @@ public class ViewKeyCertsFragment extends Fragment } Uri uri = getArguments().getParcelable(ARG_DATA_URI); - mBaseUri = KeychainContract.Certs.buildCertsUri(uri); + mBaseUri = Certs.buildCertsUri(uri); mStickyList.setAreHeadersSticky(true); mStickyList.setDrawingListUnderStickyHeader(false); @@ -185,7 +185,7 @@ public class ViewKeyCertsFragment extends Fragment public void onItemClick(AdapterView adapterView, View view, int position, long id) { Intent viewIntent = null; viewIntent = new Intent(getActivity(), ViewCertActivity.class); - viewIntent.setData(KeychainContract.Certs.buildCertsUri(Long.toString(id))); + viewIntent.setData(Certs.buildCertsUri(Long.toString(id))); startActivity(viewIntent); } @@ -230,12 +230,12 @@ public class ViewKeyCertsFragment extends Fragment private void initIndex(Cursor cursor) { if (cursor != null) { - mIndexCertId = cursor.getColumnIndexOrThrow(KeychainContract.Certs.MASTER_KEY_ID); - mIndexUserId = cursor.getColumnIndexOrThrow(KeychainContract.UserIds.USER_ID); - mIndexRank = cursor.getColumnIndexOrThrow(KeychainContract.UserIds.RANK); - mIndexVerified = cursor.getColumnIndexOrThrow(KeychainContract.Certs.VERIFIED); - mIndexSignerKeyId = cursor.getColumnIndexOrThrow(KeychainContract.Certs.KEY_ID_CERTIFIER); - mIndexSignerUserId = cursor.getColumnIndexOrThrow(KeychainContract.Certs.SIGNER_UID); + mIndexCertId = cursor.getColumnIndexOrThrow(Certs.MASTER_KEY_ID); + mIndexUserId = cursor.getColumnIndexOrThrow(Certs.USER_ID); + mIndexRank = cursor.getColumnIndexOrThrow(Certs.RANK); + mIndexVerified = cursor.getColumnIndexOrThrow(Certs.VERIFIED); + mIndexSignerKeyId = cursor.getColumnIndexOrThrow(Certs.KEY_ID_CERTIFIER); + mIndexSignerUserId = cursor.getColumnIndexOrThrow(Certs.SIGNER_UID); } } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java index b5a800712..e275dca59 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java @@ -39,7 +39,6 @@ import com.beardedhen.androidbootstrap.BootstrapButton; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; -import org.sufficientlysecure.keychain.provider.KeychainDatabase; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; @@ -176,7 +175,7 @@ public class ViewKeyMainFragment extends Fragment implements static final int INDEX_UNIFIED_EXPIRY = 8; static final String[] USER_IDS_PROJECTION = new String[] { - UserIds._ID, UserIds.USER_ID, UserIds.RANK, + UserIds._ID, UserIds.USER_ID, UserIds.RANK, UserIds.VERIFIED }; static final String[] KEYS_PROJECTION = new String[] { diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java index 2677a1a1a..772486384 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java @@ -84,7 +84,7 @@ public class ViewKeyUserIdsAdapter extends CursorAdapter { if (cursor != null) { mIndexUserId = cursor.getColumnIndexOrThrow(UserIds.USER_ID); mIndexRank = cursor.getColumnIndexOrThrow(UserIds.RANK); - // mVerifiedId = cursor.getColumnIndexOrThrow(UserIds.VERIFIED); + mVerifiedId = cursor.getColumnIndexOrThrow(UserIds.VERIFIED); } } @@ -106,10 +106,12 @@ public class ViewKeyUserIdsAdapter extends CursorAdapter { } vAddress.setText(userId[1]); - int verified = 1; // cursor.getInt(mVerifiedId); - // TODO introduce own resource for this :) - if(verified > 0) + int verified = cursor.getInt(mVerifiedId); + // TODO introduce own resources for this :) + if(verified == 1) vVerified.setImageResource(android.R.drawable.presence_online); + else if(verified > 1) + vVerified.setImageResource(android.R.drawable.presence_away); else vVerified.setImageResource(android.R.drawable.presence_invisible); From 1b1304e6e043e95d9e9f5d7082c0a47c5d9ad865 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sat, 5 Apr 2014 22:35:27 +0200 Subject: [PATCH 19/30] certs: various improvements * recognize self-certifications * save signature type * save expiry time * drop encoded data blob from db --- .../keychain/provider/KeychainContract.java | 13 +++-- .../keychain/provider/KeychainDatabase.java | 4 +- .../keychain/provider/KeychainProvider.java | 4 +- .../keychain/provider/ProviderHelper.java | 52 +++++++++++++------ .../keychain/ui/ViewCertActivity.java | 3 +- .../ui/adapter/ViewKeyUserIdsAdapter.java | 9 ++-- 6 files changed, 55 insertions(+), 30 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java index 2b40300d7..4a2dd2da2 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java @@ -55,13 +55,13 @@ public class KeychainContract { } interface CertsColumns { - String MASTER_KEY_ID = "master_key_id"; // verified id, foreign key to key_rings._ID - String RANK = "rank"; // rank of verified key - String KEY_ID_CERTIFIER = "key_id_certifier"; // verifying id, not a database id + String MASTER_KEY_ID = "master_key_id"; + String RANK = "rank"; + String KEY_ID_CERTIFIER = "key_id_certifier"; + String TYPE = "type"; + String VERIFIED = "verified"; String CREATION = "creation"; String EXPIRY = "expiry"; - String VERIFIED = "verified"; - String KEY_DATA = "key_data"; // certification blob } interface ApiAppsColumns { @@ -262,6 +262,9 @@ public class KeychainContract { public static final String USER_ID = UserIdsColumns.USER_ID; public static final String SIGNER_UID = "signer_user_id"; + public static final int VERIFIED_SECRET = 1; + public static final int VERIFIED_SELF = 2; + public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() .appendPath(BASE_KEY_RINGS).build(); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java index fda1783cb..c704da0e7 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java @@ -110,11 +110,11 @@ public class KeychainDatabase extends SQLiteOpenHelper { + CertsColumns.RANK + " INTEGER, " // rank of certified uid + CertsColumns.KEY_ID_CERTIFIER + " INTEGER, " // certifying key + + CertsColumns.TYPE + " INTEGER, " + + CertsColumns.VERIFIED + " INTEGER, " + CertsColumns.CREATION + " INTEGER, " + CertsColumns.EXPIRY + " INTEGER, " - + CertsColumns.VERIFIED + " INTEGER, " - + CertsColumns.KEY_DATA + " BLOB," + "PRIMARY KEY(" + CertsColumns.MASTER_KEY_ID + ", " + CertsColumns.RANK + ", " + CertsColumns.KEY_ID_CERTIFIER + "), " + "FOREIGN KEY(" + CertsColumns.MASTER_KEY_ID + ") REFERENCES " diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java index 01120eedf..0b15187dd 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -439,10 +439,10 @@ public class KeychainProvider extends ContentProvider { projectionMap.put(Certs._ID, Tables.CERTS + ".oid AS " + Certs._ID); projectionMap.put(Certs.MASTER_KEY_ID, Tables.CERTS + "." + Certs.MASTER_KEY_ID); projectionMap.put(Certs.RANK, Tables.CERTS + "." + Certs.RANK); + projectionMap.put(Certs.VERIFIED, Tables.CERTS + "." + Certs.VERIFIED); + projectionMap.put(Certs.TYPE, Tables.CERTS + "." + Certs.TYPE); projectionMap.put(Certs.CREATION, Tables.CERTS + "." + Certs.CREATION); projectionMap.put(Certs.KEY_ID_CERTIFIER, Tables.CERTS + "." + Certs.KEY_ID_CERTIFIER); - projectionMap.put(Certs.VERIFIED, Tables.CERTS + "." + Certs.VERIFIED); - projectionMap.put(Certs.KEY_DATA, Tables.CERTS + "." + Certs.KEY_DATA); projectionMap.put(Certs.USER_ID, Tables.USER_IDS + "." + UserIds.USER_ID); projectionMap.put(Certs.SIGNER_UID, "signer." + UserIds.USER_ID + " AS " + Certs.SIGNER_UID); qb.setProjectionMap(projectionMap); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index 02d8d29f2..1b653ae06 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -29,6 +29,8 @@ import android.database.DatabaseUtils; import android.net.Uri; import android.os.RemoteException; +import org.spongycastle.bcpg.SignatureSubpacketTags; +import org.spongycastle.bcpg.sig.SignatureExpirationTime; import org.spongycastle.openpgp.PGPException; import org.spongycastle.openpgp.PGPSecretKey; import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider; @@ -231,22 +233,37 @@ public class ProviderHelper { for (String userId : new IterableIterator(masterKey.getUserIDs())) { operations.add(buildUserIdOperations(context, masterKeyId, userId, userIdRank)); + // HashMap certs = new HashMap(); + // look through signatures for this specific key for (PGPSignature cert : new IterableIterator( masterKey.getSignaturesForID(userId))) { long certId = cert.getKeyID(); - boolean verified = false; - // do verify signatures from our own private keys - if(allKeyRings.containsKey(certId)) try { - // mark them as verified - cert.init( - new JcaPGPContentVerifierBuilderProvider().setProvider( + int verified = 0; + // verify from the key itself + try { + // verify signatures from known private keys + if(allKeyRings.containsKey(certId)) { + // mark them as verified + cert.init( + new JcaPGPContentVerifierBuilderProvider().setProvider( + Constants.BOUNCY_CASTLE_PROVIDER_NAME), + allKeyRings.get(certId).getPublicKey()); + verified = cert.verifyCertification(userId, masterKey) ? Certs.VERIFIED_SECRET : 0; + Log.d(Constants.TAG, "Verified sig for " + userId + " " + verified + " from " + + PgpKeyHelper.convertKeyIdToHex(certId) + ); + // if that didn't work out, is it at least an own signature? + } else if(certId == masterKeyId) { + cert.init( + new JcaPGPContentVerifierBuilderProvider().setProvider( Constants.BOUNCY_CASTLE_PROVIDER_NAME), - allKeyRings.get(certId).getPublicKey()); - verified = cert.verifyCertification(userId, masterKey); - Log.d(Constants.TAG, "Verified sig for " + userId + " " + verified + " from " - + PgpKeyHelper.convertKeyIdToHex(cert.getKeyID()) - ); + masterKey); + verified = cert.verifyCertification(userId, masterKey) ? Certs.VERIFIED_SELF : 0; + Log.d(Constants.TAG, "Verified sig for " + userId + " " + verified + " from " + + PgpKeyHelper.convertKeyIdToHex(certId) + ); + } } catch(SignatureException e) { Log.e(Constants.TAG, "Signature verification failed! " + PgpKeyHelper.convertKeyIdToHex(masterKey.getKeyID()) @@ -263,7 +280,7 @@ public class ProviderHelper { ); // regardless of verification, save the certification operations.add(buildCertOperations( - context, masterKeyId, userIdRank, masterKey.getKeyID(), cert, verified)); + context, masterKeyId, userIdRank, cert, verified)); } ++userIdRank; @@ -354,18 +371,21 @@ public class ProviderHelper { private static ContentProviderOperation buildCertOperations(Context context, long masterKeyId, int rank, - long keyId, PGPSignature cert, - boolean verified) + int verified) throws IOException { ContentValues values = new ContentValues(); values.put(Certs.MASTER_KEY_ID, masterKeyId); values.put(Certs.RANK, rank); values.put(Certs.KEY_ID_CERTIFIER, cert.getKeyID()); + values.put(Certs.TYPE, cert.getSignatureType()); values.put(Certs.CREATION, cert.getCreationTime().getTime() / 1000); - values.put(Certs.EXPIRY, (String) null); // TODO + if(cert.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.EXPIRE_TIME)) { + long ext = ((SignatureExpirationTime) cert.getHashedSubPackets().getSubpacket( + SignatureSubpacketTags.EXPIRE_TIME)).getTime(); + values.put(Certs.EXPIRY, cert.getCreationTime().getTime() / 1000 + ext); + } values.put(Certs.VERIFIED, verified); - values.put(Certs.KEY_DATA, cert.getEncoded()); Uri uri = Certs.buildCertsUri(Long.toString(masterKeyId)); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java index b273955dd..ca548ac34 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java @@ -57,7 +57,7 @@ public class ViewCertActivity extends ActionBarActivity Certs.CREATION, Certs.KEY_ID_CERTIFIER, Certs.SIGNER_UID, - Certs.KEY_DATA + Certs.TYPE }; private static final int INDEX_MASTER_KEY_ID = 1; private static final int INDEX_USER_ID = 2; @@ -65,6 +65,7 @@ public class ViewCertActivity extends ActionBarActivity private static final int INDEX_KEY_ID_CERTIFIER = 4; private static final int INDEX_UID_CERTIFIER = 5; private static final int INDEX_KEY_DATA = 6; + private static final int INDEX_KEY_TYPE = 6; private Uri mDataUri; diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java index 772486384..ba304436b 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java @@ -30,6 +30,7 @@ import android.widget.TextView; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; +import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; import java.util.ArrayList; @@ -108,12 +109,12 @@ public class ViewKeyUserIdsAdapter extends CursorAdapter { int verified = cursor.getInt(mVerifiedId); // TODO introduce own resources for this :) - if(verified == 1) + if(verified == Certs.VERIFIED_SECRET) vVerified.setImageResource(android.R.drawable.presence_online); - else if(verified > 1) - vVerified.setImageResource(android.R.drawable.presence_away); - else + else if(verified == Certs.VERIFIED_SELF) vVerified.setImageResource(android.R.drawable.presence_invisible); + else + vVerified.setImageResource(android.R.drawable.presence_busy); // don't care further if checkboxes aren't shown if (mCheckStates == null) { From 437da180fc6bd427a50ef0cc65b9b379cbd646d3 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sun, 6 Apr 2014 00:58:11 +0200 Subject: [PATCH 20/30] certs: properly handle primary flag and revocation of uids --- .../keychain/provider/KeychainContract.java | 4 +- .../keychain/provider/KeychainDatabase.java | 1 + .../keychain/provider/KeychainProvider.java | 2 +- .../keychain/provider/ProviderHelper.java | 113 ++++++++++++------ .../keychain/ui/ViewCertActivity.java | 53 ++++---- 5 files changed, 108 insertions(+), 65 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java index 4a2dd2da2..28d54d818 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java @@ -52,6 +52,7 @@ public class KeychainContract { String USER_ID = "user_id"; // not a database id String RANK = "rank"; // ONLY used for sorting! no key, no nothing! String IS_PRIMARY = "is_primary"; + String IS_REVOKED = "is_revoked"; } interface CertsColumns { @@ -107,7 +108,8 @@ public class KeychainContract { public static final String PATH_ACCOUNTS = "accounts"; public static class KeyRings implements BaseColumns, KeysColumns, UserIdsColumns { - public static final String MASTER_KEY_ID = "master_key_id"; + public static final String MASTER_KEY_ID = KeysColumns.MASTER_KEY_ID; + public static final String IS_REVOKED = KeysColumns.IS_REVOKED; public static final String HAS_SECRET = "has_secret"; public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java index c704da0e7..2bd1c13a0 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java @@ -96,6 +96,7 @@ public class KeychainDatabase extends SQLiteOpenHelper { + UserIdsColumns.USER_ID + " CHARMANDER, " + UserIdsColumns.IS_PRIMARY + " BOOLEAN, " + + UserIdsColumns.IS_REVOKED + " BOOLEAN, " + UserIdsColumns.RANK+ " INTEGER, " + "PRIMARY KEY(" + UserIdsColumns.MASTER_KEY_ID + ", " + UserIdsColumns.USER_ID + "), " diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java index 0b15187dd..7365005e4 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -249,7 +249,7 @@ public class KeychainProvider extends ContentProvider { projectionMap.put(KeyRings.MASTER_KEY_ID, Tables.KEYS + "." + Keys.MASTER_KEY_ID); projectionMap.put(KeyRings.KEY_ID, Keys.KEY_ID); projectionMap.put(KeyRings.KEY_SIZE, Keys.KEY_SIZE); - projectionMap.put(KeyRings.IS_REVOKED, Keys.IS_REVOKED); + projectionMap.put(KeyRings.IS_REVOKED, Tables.KEYS + "." + Keys.IS_REVOKED); projectionMap.put(KeyRings.CAN_CERTIFY, Keys.CAN_CERTIFY); projectionMap.put(KeyRings.CAN_ENCRYPT, Keys.CAN_ENCRYPT); projectionMap.put(KeyRings.CAN_SIGN, Keys.CAN_SIGN); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index 1b653ae06..b2d41a151 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -32,7 +32,6 @@ import android.os.RemoteException; import org.spongycastle.bcpg.SignatureSubpacketTags; import org.spongycastle.bcpg.sig.SignatureExpirationTime; import org.spongycastle.openpgp.PGPException; -import org.spongycastle.openpgp.PGPSecretKey; import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider; import org.spongycastle.openpgp.PGPKeyRing; import org.spongycastle.openpgp.PGPPublicKey; @@ -57,8 +56,10 @@ import org.sufficientlysecure.keychain.util.Log; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.ArrayList; +import java.util.Collections; import java.util.Date; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.HashSet; import java.util.Set; @@ -229,19 +230,40 @@ public class ProviderHelper { if(secretRing != null) allKeyRings.put(secretRing.getSecretKey().getKeyID(), secretRing); - int userIdRank = 0; - for (String userId : new IterableIterator(masterKey.getUserIDs())) { - operations.add(buildUserIdOperations(context, masterKeyId, userId, userIdRank)); + // classify and order user ids. primary are moved to the front, revoked to the back, + // otherwise the order in the keyfile is preserved. + List uids = new ArrayList(); - // HashMap certs = new HashMap(); + for (String userId : new IterableIterator(masterKey.getUserIDs())) { + UserIdItem item = new UserIdItem(); + uids.add(item); + item.userId = userId; // look through signatures for this specific key for (PGPSignature cert : new IterableIterator( masterKey.getSignaturesForID(userId))) { long certId = cert.getKeyID(); - int verified = 0; - // verify from the key itself try { + // self signature + if(certId == masterKeyId) { + cert.init( + new JcaPGPContentVerifierBuilderProvider().setProvider( + Constants.BOUNCY_CASTLE_PROVIDER_NAME), + masterKey); + if(!cert.verifyCertification(userId, masterKey)) { + // not verified?! dang! TODO notify user? this is kinda serious... + Log.e(Constants.TAG, "Could not verify self signature for " + userId + "!"); + continue; + } + // is this the first, or a more recent certificate? + if(item.selfCert == null || + item.selfCert.getCreationTime().before(cert.getCreationTime())) { + item.selfCert = cert; + item.isPrimary = cert.getHashedSubPackets().isPrimaryUserID(); + item.isRevoked = + cert.getSignatureType() == PGPSignature.CERTIFICATION_REVOCATION; + } + } // verify signatures from known private keys if(allKeyRings.containsKey(certId)) { // mark them as verified @@ -249,20 +271,9 @@ public class ProviderHelper { new JcaPGPContentVerifierBuilderProvider().setProvider( Constants.BOUNCY_CASTLE_PROVIDER_NAME), allKeyRings.get(certId).getPublicKey()); - verified = cert.verifyCertification(userId, masterKey) ? Certs.VERIFIED_SECRET : 0; - Log.d(Constants.TAG, "Verified sig for " + userId + " " + verified + " from " - + PgpKeyHelper.convertKeyIdToHex(certId) - ); - // if that didn't work out, is it at least an own signature? - } else if(certId == masterKeyId) { - cert.init( - new JcaPGPContentVerifierBuilderProvider().setProvider( - Constants.BOUNCY_CASTLE_PROVIDER_NAME), - masterKey); - verified = cert.verifyCertification(userId, masterKey) ? Certs.VERIFIED_SELF : 0; - Log.d(Constants.TAG, "Verified sig for " + userId + " " + verified + " from " - + PgpKeyHelper.convertKeyIdToHex(certId) - ); + if(cert.verifyCertification(userId, masterKey)) { + item.trustedCerts.add(cert); + } } } catch(SignatureException e) { Log.e(Constants.TAG, "Signature verification failed! " @@ -275,15 +286,25 @@ public class ProviderHelper { + " from " + PgpKeyHelper.convertKeyIdToHex(cert.getKeyID()), e); } - Log.d(Constants.TAG, "sig for " + userId + " from " - + PgpKeyHelper.convertKeyIdToHex(cert.getKeyID()) - ); - // regardless of verification, save the certification - operations.add(buildCertOperations( - context, masterKeyId, userIdRank, cert, verified)); } + } - ++userIdRank; + // primary before regular before revoked (see UserIdItem.compareTo) + // this is a stable sort, so the order of keys is otherwise preserved. + Collections.sort(uids); + // iterate and put into db + 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, Certs.VERIFIED_SELF)); + } + for(int i = 0; i < item.trustedCerts.size(); i++) { + operations.add(buildCertOperations( + masterKeyId, userIdRank, item.trustedCerts.get(i), Certs.VERIFIED_SECRET)); + } } try { @@ -301,6 +322,25 @@ public class ProviderHelper { } + private static class UserIdItem implements Comparable { + String userId; + boolean isPrimary = false; + boolean isRevoked = false; + PGPSignature selfCert; + List trustedCerts = new ArrayList(); + + @Override + public int compareTo(UserIdItem o) { + // if one key is primary but the other isn't, the primary one always comes first + if(isPrimary != o.isPrimary) + return isPrimary ? -1 : 1; + // revoked keys always come last! + if(isRevoked != o.isRevoked) + return isRevoked ? 1 : -1; + return 0; + } + } + /** * Saves a PGPSecretKeyRing in the DB. This will only work if a corresponding public keyring * is already in the database! @@ -368,11 +408,10 @@ public class ProviderHelper { /** * Build ContentProviderOperation to add PGPPublicKey to database corresponding to a keyRing */ - private static ContentProviderOperation buildCertOperations(Context context, - long masterKeyId, - int rank, - PGPSignature cert, - int verified) + private static ContentProviderOperation buildCertOperations(long masterKeyId, + int rank, + PGPSignature cert, + int verified) throws IOException { ContentValues values = new ContentValues(); values.put(Certs.MASTER_KEY_ID, masterKeyId); @@ -395,11 +434,13 @@ public class ProviderHelper { /** * Build ContentProviderOperation to add PublicUserIds to database corresponding to a keyRing */ - private static ContentProviderOperation buildUserIdOperations(Context context, - long masterKeyId, String userId, int rank) { + private static ContentProviderOperation buildUserIdOperations(long masterKeyId, UserIdItem item, + int rank) { ContentValues values = new ContentValues(); values.put(UserIds.MASTER_KEY_ID, masterKeyId); - values.put(UserIds.USER_ID, userId); + values.put(UserIds.USER_ID, item.userId); + values.put(UserIds.IS_PRIMARY, item.isPrimary); + values.put(UserIds.IS_REVOKED, item.isRevoked); values.put(UserIds.RANK, rank); Uri uri = UserIds.buildUserIdsUri(Long.toString(masterKeyId)); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java index ca548ac34..e5f2fb173 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java @@ -54,18 +54,21 @@ public class ViewCertActivity extends ActionBarActivity static final String[] PROJECTION = new String[] { Certs.MASTER_KEY_ID, Certs.USER_ID, + Certs.TYPE, Certs.CREATION, + Certs.EXPIRY, Certs.KEY_ID_CERTIFIER, Certs.SIGNER_UID, Certs.TYPE }; private static final int INDEX_MASTER_KEY_ID = 1; private static final int INDEX_USER_ID = 2; - private static final int INDEX_CREATION = 3; - private static final int INDEX_KEY_ID_CERTIFIER = 4; - private static final int INDEX_UID_CERTIFIER = 5; - private static final int INDEX_KEY_DATA = 6; - private static final int INDEX_KEY_TYPE = 6; + private static final int INDEX_TYPE = 3; + private static final int INDEX_CREATION = 4; + private static final int INDEX_EXPIRY = 5; + private static final int INDEX_KEY_ID_CERTIFIER = 6; + private static final int INDEX_UID_CERTIFIER = 7; + private static final int INDEX_KEY_TYPE = 8; private Uri mDataUri; @@ -133,30 +136,26 @@ public class ViewCertActivity extends ActionBarActivity else mSignerUid.setText(R.string.unknown_uid); - byte[] sigData = data.getBlob(INDEX_KEY_DATA); - PGPSignature sig = PgpConversionHelper.BytesToPGPSignature(sigData); - if(sig != null) { - String algorithmStr = PgpKeyHelper.getAlgorithmInfo(sig.getKeyAlgorithm(), 0); - mAlgorithm.setText(algorithmStr); + // String algorithmStr = PgpKeyHelper.getAlgorithmInfo(sig.getKeyAlgorithm(), 0); + // mAlgorithm.setText(algorithmStr); - switch(sig.getSignatureType()) { - case PGPSignature.DEFAULT_CERTIFICATION: - mType.setText(R.string.sig_type_default); break; - case PGPSignature.NO_CERTIFICATION: - mType.setText(R.string.sig_type_none); break; - case PGPSignature.CASUAL_CERTIFICATION: - mType.setText(R.string.sig_type_casual); break; - case PGPSignature.POSITIVE_CERTIFICATION: - mType.setText(R.string.sig_type_positive); break; - } + switch(data.getInt(INDEX_TYPE)) { + case PGPSignature.DEFAULT_CERTIFICATION: + mType.setText(R.string.sig_type_default); break; + case PGPSignature.NO_CERTIFICATION: + mType.setText(R.string.sig_type_none); break; + case PGPSignature.CASUAL_CERTIFICATION: + mType.setText(R.string.sig_type_casual); break; + case PGPSignature.POSITIVE_CERTIFICATION: + mType.setText(R.string.sig_type_positive); break; + } - long expiry = sig.getHashedSubPackets().getSignatureExpirationTime(); - if(expiry == 0) - mExpiry.setText("never"); - else { - Date expiryDate = new Date(creationDate.getTime() + expiry * 1000); - mExpiry.setText(DateFormat.getDateFormat(getApplicationContext()).format(expiryDate)); - } + long expiry = data.getLong(INDEX_EXPIRY); + if(expiry == 0) + mExpiry.setText("never"); + else { + Date expiryDate = new Date(creationDate.getTime() + expiry * 1000); + mExpiry.setText(DateFormat.getDateFormat(getApplicationContext()).format(expiryDate)); } } } From ed3798be8ab8c1f5bebd4198f55c3a253956c9cc Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sun, 6 Apr 2014 01:11:57 +0200 Subject: [PATCH 21/30] certs: display uid status in uid list --- .../keychain/provider/KeychainProvider.java | 1 + .../keychain/provider/ProviderHelper.java | 4 +++ .../keychain/ui/ViewKeyMainFragment.java | 3 +- .../ui/adapter/ViewKeyUserIdsAdapter.java | 30 ++++++++++++------- .../main/res/layout/view_key_certs_header.xml | 2 +- 5 files changed, 28 insertions(+), 12 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java index 7365005e4..903c27859 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -373,6 +373,7 @@ public class KeychainProvider extends ContentProvider { projectionMap.put(UserIds.USER_ID, Tables.USER_IDS + "." + UserIds.USER_ID); projectionMap.put(UserIds.RANK, Tables.USER_IDS + "." + UserIds.RANK); projectionMap.put(UserIds.IS_PRIMARY, Tables.USER_IDS + "." + UserIds.IS_PRIMARY); + projectionMap.put(UserIds.IS_REVOKED, Tables.USER_IDS + "." + UserIds.IS_REVOKED); // we take the minimum (>0) here, where "1" is "verified by known secret key" projectionMap.put(UserIds.VERIFIED, "MIN(" + Certs.VERIFIED + ") AS " + UserIds.VERIFIED); qb.setProjectionMap(projectionMap); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index b2d41a151..78d61a521 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -301,6 +301,10 @@ public class ProviderHelper { operations.add(buildCertOperations( masterKeyId, userIdRank, item.selfCert, Certs.VERIFIED_SELF)); } + // don't bother with trusted certs if the uid is revoked, anyways + if(item.isRevoked) { + continue; + } for(int i = 0; i < item.trustedCerts.size(); i++) { operations.add(buildCertOperations( masterKeyId, userIdRank, item.trustedCerts.get(i), Certs.VERIFIED_SECRET)); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java index e275dca59..60df6970d 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java @@ -175,7 +175,8 @@ public class ViewKeyMainFragment extends Fragment implements static final int INDEX_UNIFIED_EXPIRY = 8; static final String[] USER_IDS_PROJECTION = new String[] { - UserIds._ID, UserIds.USER_ID, UserIds.RANK, UserIds.VERIFIED + UserIds._ID, UserIds.USER_ID, UserIds.RANK, + UserIds.VERIFIED, UserIds.IS_PRIMARY, UserIds.IS_REVOKED }; static final String[] KEYS_PROJECTION = new String[] { diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java index ba304436b..023d3d660 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java @@ -38,7 +38,7 @@ public class ViewKeyUserIdsAdapter extends CursorAdapter { private LayoutInflater mInflater; private int mIndexUserId, mIndexRank; - private int mVerifiedId; + private int mVerifiedId, mIsRevoked, mIsPrimary; private final ArrayList mCheckStates; @@ -86,6 +86,8 @@ public class ViewKeyUserIdsAdapter extends CursorAdapter { mIndexUserId = cursor.getColumnIndexOrThrow(UserIds.USER_ID); mIndexRank = cursor.getColumnIndexOrThrow(UserIds.RANK); mVerifiedId = cursor.getColumnIndexOrThrow(UserIds.VERIFIED); + mIsRevoked = cursor.getColumnIndexOrThrow(UserIds.IS_REVOKED); + mIsPrimary = cursor.getColumnIndexOrThrow(UserIds.IS_PRIMARY); } } @@ -97,7 +99,11 @@ public class ViewKeyUserIdsAdapter extends CursorAdapter { TextView vAddress = (TextView) view.findViewById(R.id.address); ImageView vVerified = (ImageView) view.findViewById(R.id.certified); - vRank.setText(Integer.toString(cursor.getInt(mIndexRank))); + if(cursor.getInt(mIsPrimary) > 0) { + vRank.setText("+"); + } else { + vRank.setText(Integer.toString(cursor.getInt(mIndexRank))); + } String[] userId = PgpKeyHelper.splitUserId(cursor.getString(mIndexUserId)); if (userId[0] != null) { @@ -107,14 +113,18 @@ public class ViewKeyUserIdsAdapter extends CursorAdapter { } vAddress.setText(userId[1]); - int verified = cursor.getInt(mVerifiedId); - // TODO introduce own resources for this :) - if(verified == Certs.VERIFIED_SECRET) - vVerified.setImageResource(android.R.drawable.presence_online); - else if(verified == Certs.VERIFIED_SELF) - vVerified.setImageResource(android.R.drawable.presence_invisible); - else - vVerified.setImageResource(android.R.drawable.presence_busy); + if(cursor.getInt(mIsRevoked) > 0) { + vVerified.setImageResource(android.R.drawable.presence_away); + } else { + int verified = cursor.getInt(mVerifiedId); + // TODO introduce own resources for this :) + if(verified == Certs.VERIFIED_SECRET) + vVerified.setImageResource(android.R.drawable.presence_online); + else if(verified == Certs.VERIFIED_SELF) + vVerified.setImageResource(android.R.drawable.presence_invisible); + else + vVerified.setImageResource(android.R.drawable.presence_busy); + } // don't care further if checkboxes aren't shown if (mCheckStates == null) { diff --git a/OpenPGP-Keychain/src/main/res/layout/view_key_certs_header.xml b/OpenPGP-Keychain/src/main/res/layout/view_key_certs_header.xml index de481c1cc..04e7b8097 100644 --- a/OpenPGP-Keychain/src/main/res/layout/view_key_certs_header.xml +++ b/OpenPGP-Keychain/src/main/res/layout/view_key_certs_header.xml @@ -10,7 +10,7 @@ android:layout_gravity="start|left" android:padding="8dp" android:textColor="@color/emphasis" - android:textSize="17sp" + android:textSize="14sp" android:textStyle="bold" android:text="header text" /> From 84857df703c2ffe941bc803669f8fd8d04270869 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sun, 6 Apr 2014 01:25:52 +0200 Subject: [PATCH 22/30] certs: display cert type in cert view --- .../keychain/ui/ViewKeyCertsFragment.java | 30 ++++++++++++------- .../src/main/res/values/strings.xml | 5 ++++ 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java index 1696d18dd..eb2efcfa7 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java @@ -35,6 +35,7 @@ import android.widget.ArrayAdapter; import android.widget.Spinner; import android.widget.TextView; +import org.spongycastle.openpgp.PGPSignature; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; @@ -55,6 +56,7 @@ public class ViewKeyCertsFragment extends Fragment Certs._ID, Certs.MASTER_KEY_ID, Certs.VERIFIED, + Certs.TYPE, Certs.RANK, Certs.KEY_ID_CERTIFIER, Certs.USER_ID, @@ -64,8 +66,7 @@ public class ViewKeyCertsFragment extends Fragment // sort by our user id, static final String SORT_ORDER = Tables.CERTS + "." + Certs.RANK + " ASC, " - + Certs.VERIFIED + " DESC, " - + Certs.SIGNER_UID + " ASC"; + + Certs.VERIFIED + " DESC, " + Certs.TYPE + " DESC, " + Certs.SIGNER_UID + " ASC"; public static final String ARG_DATA_URI = "data_uri"; @@ -73,9 +74,8 @@ public class ViewKeyCertsFragment extends Fragment private Spinner mSpinner; private CertListAdapter mAdapter; - private boolean mUnknownShown = false; - private Uri mBaseUri, mDataUri; + private Uri mDataUri; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -133,7 +133,7 @@ public class ViewKeyCertsFragment extends Fragment } Uri uri = getArguments().getParcelable(ARG_DATA_URI); - mBaseUri = Certs.buildCertsUri(uri); + mDataUri = Certs.buildCertsUri(uri); mStickyList.setAreHeadersSticky(true); mStickyList.setDrawingListUnderStickyHeader(false); @@ -155,9 +155,6 @@ public class ViewKeyCertsFragment extends Fragment mAdapter = new CertListAdapter(getActivity(), null); mStickyList.setAdapter(mAdapter); - // Prepare the loader. Either re-connect with an existing one, - // or start a new one. - mDataUri = mBaseUri; getLoaderManager().initLoader(0, null, this); } @@ -205,7 +202,7 @@ public class ViewKeyCertsFragment extends Fragment private int mIndexCertId; private int mIndexUserId, mIndexRank; private int mIndexSignerKeyId, mIndexSignerUserId; - private int mIndexVerified; + private int mIndexVerified, mIndexType; public CertListAdapter(Context context, Cursor c) { super(context, c, 0); @@ -233,6 +230,7 @@ public class ViewKeyCertsFragment extends Fragment mIndexCertId = cursor.getColumnIndexOrThrow(Certs.MASTER_KEY_ID); mIndexUserId = cursor.getColumnIndexOrThrow(Certs.USER_ID); mIndexRank = cursor.getColumnIndexOrThrow(Certs.RANK); + mIndexType = cursor.getColumnIndexOrThrow(Certs.TYPE); mIndexVerified = cursor.getColumnIndexOrThrow(Certs.VERIFIED); mIndexSignerKeyId = cursor.getColumnIndexOrThrow(Certs.KEY_ID_CERTIFIER); mIndexSignerUserId = cursor.getColumnIndexOrThrow(Certs.SIGNER_UID); @@ -255,11 +253,21 @@ public class ViewKeyCertsFragment extends Fragment String signerKeyId = PgpKeyHelper.convertKeyIdToHex(cursor.getLong(mIndexSignerKeyId)); String signerUserId = cursor.getString(mIndexSignerUserId); - String signStatus = cursor.getInt(mIndexVerified) > 0 ? "ok" : "unknown"; + switch(cursor.getInt(mIndexType)) { + case PGPSignature.DEFAULT_CERTIFICATION: // 0x10 + wSignStatus.setText(R.string.cert_default); break; + case PGPSignature.NO_CERTIFICATION: // 0x11 + wSignStatus.setText(R.string.cert_none); break; + case PGPSignature.CASUAL_CERTIFICATION: // 0x12 + wSignStatus.setText(R.string.cert_casual); break; + case PGPSignature.POSITIVE_CERTIFICATION: // 0x13 + wSignStatus.setText(R.string.cert_positive); break; + case PGPSignature.CERTIFICATION_REVOCATION: // 0x30 + wSignStatus.setText(R.string.cert_revoke); break; + } wSignerUserId.setText(signerUserId); wSignerKeyId.setText(signerKeyId); - wSignStatus.setText(signStatus); } diff --git a/OpenPGP-Keychain/src/main/res/values/strings.xml b/OpenPGP-Keychain/src/main/res/values/strings.xml index 55971003b..e678d8848 100644 --- a/OpenPGP-Keychain/src/main/res/values/strings.xml +++ b/OpenPGP-Keychain/src/main/res/values/strings.xml @@ -507,5 +507,10 @@ Write message here to encrypt and/or sign… Enter ciphertext here to decrypt and/or verify… + default + none + casual + positive + revoke From 120e1eada729c34bf138138b7df4e6827acb6535 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sun, 6 Apr 2014 01:57:28 +0200 Subject: [PATCH 23/30] certs: ditch selection spinner from certs view --- .../keychain/ui/ViewKeyCertsFragment.java | 38 ------------------- .../res/layout/view_key_certs_fragment.xml | 13 +------ 2 files changed, 1 insertion(+), 50 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java index eb2efcfa7..325e9e179 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java @@ -71,8 +71,6 @@ public class ViewKeyCertsFragment extends Fragment public static final String ARG_DATA_URI = "data_uri"; private StickyListHeadersListView mStickyList; - private Spinner mSpinner; - private CertListAdapter mAdapter; private Uri mDataUri; @@ -84,46 +82,10 @@ public class ViewKeyCertsFragment extends Fragment return view; } - private void changeShowState(int type) { - switch(type) { - case 0: - mDataUri = mBaseUri.buildUpon().appendPath("has_secret").build(); - break; - case 1: - mDataUri = mBaseUri; - break; - case 2: - mDataUri = mBaseUri.buildUpon().appendPath("all").build(); - break; - } - getLoaderManager().restartLoader(0, null, this); - } - @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - mSpinner = (Spinner) getActivity().findViewById(R.id.spinner); - ArrayAdapter adapter = new ArrayAdapter(getActivity(), - android.R.layout.simple_spinner_item, new String[] { - getResources().getString(R.string.certs_list_known_secret), - getResources().getString(R.string.certs_list_known), - getResources().getString(R.string.certs_list_all) - } ); - adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - mSpinner.setAdapter(adapter); - mSpinner.setSelection(1); - mSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { - @Override - public void onItemSelected(AdapterView adapterView, View view, int i, long l) { - changeShowState(i); - } - - @Override - public void onNothingSelected(AdapterView adapterView) { - } - }); - mStickyList = (StickyListHeadersListView) getActivity().findViewById(R.id.list); if (!getArguments().containsKey(ARG_DATA_URI)) { diff --git a/OpenPGP-Keychain/src/main/res/layout/view_key_certs_fragment.xml b/OpenPGP-Keychain/src/main/res/layout/view_key_certs_fragment.xml index 032b9eee6..33042c541 100644 --- a/OpenPGP-Keychain/src/main/res/layout/view_key_certs_fragment.xml +++ b/OpenPGP-Keychain/src/main/res/layout/view_key_certs_fragment.xml @@ -1,5 +1,4 @@ @@ -9,16 +8,6 @@ android:layout_height="wrap_content" android:orientation="vertical"> - - + android:layout_below="@+id/list" /> Date: Sun, 6 Apr 2014 02:27:21 +0200 Subject: [PATCH 24/30] certs: fix CertifyKeyActivity and improve user id list --- .../keychain/ui/CertifyKeyActivity.java | 32 ++++++++----------- .../keychain/ui/ViewKeyMainFragment.java | 7 +--- .../ui/adapter/ViewKeyUserIdsAdapter.java | 25 ++++++++++----- .../main/res/layout/certify_key_activity.xml | 5 +-- .../main/res/layout/view_key_userids_item.xml | 7 ++-- 5 files changed, 39 insertions(+), 37 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java index 2efa8a69c..7027c114e 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java @@ -46,8 +46,8 @@ import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.helper.Preferences; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; -import org.sufficientlysecure.keychain.provider.KeychainContract; -import org.sufficientlysecure.keychain.provider.KeychainDatabase; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; +import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; @@ -148,43 +148,37 @@ public class CertifyKeyActivity extends ActionBarActivity implements mUserIdsAdapter = new ViewKeyUserIdsAdapter(this, null, 0, true); mUserIds.setAdapter(mUserIdsAdapter); + mUserIds.setOnItemClickListener(mUserIdsAdapter); getSupportLoaderManager().initLoader(LOADER_ID_KEYRING, null, this); getSupportLoaderManager().initLoader(LOADER_ID_USER_IDS, null, this); } + static final String USER_IDS_SELECTION = UserIds.IS_REVOKED + " = 0"; + static final String[] KEYRING_PROJECTION = new String[] { - KeychainContract.KeyRings._ID, - KeychainContract.Keys.MASTER_KEY_ID, - KeychainContract.Keys.FINGERPRINT, - KeychainContract.UserIds.USER_ID, + KeyRings._ID, + KeyRings.MASTER_KEY_ID, + KeyRings.FINGERPRINT, + KeyRings.USER_ID, }; static final int INDEX_MASTER_KEY_ID = 1; static final int INDEX_FINGERPRINT = 2; static final int INDEX_USER_ID = 3; - static final String[] USER_IDS_PROJECTION = - new String[]{ - KeychainContract.UserIds._ID, - KeychainContract.UserIds.USER_ID, - KeychainContract.UserIds.RANK, - "verified" - }; - static final String USER_IDS_SORT_ORDER = - KeychainDatabase.Tables.USER_IDS + "." + KeychainContract.UserIds.RANK + " ASC"; - @Override public Loader onCreateLoader(int id, Bundle args) { switch(id) { case LOADER_ID_KEYRING: { - Uri uri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(mDataUri); + Uri uri = KeyRings.buildUnifiedKeyRingUri(mDataUri); return new CursorLoader(this, uri, KEYRING_PROJECTION, null, null, null); } case LOADER_ID_USER_IDS: { - Uri uri = KeychainContract.UserIds.buildUserIdsUri(mDataUri); - return new CursorLoader(this, uri, USER_IDS_PROJECTION, null, null, USER_IDS_SORT_ORDER); + Uri uri = UserIds.buildUserIdsUri(mDataUri); + return new CursorLoader(this, uri, + ViewKeyUserIdsAdapter.USER_IDS_PROJECTION, USER_IDS_SELECTION, null, null); } } return null; diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java index 60df6970d..d12f00547 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java @@ -174,11 +174,6 @@ public class ViewKeyMainFragment extends Fragment implements static final int INDEX_UNIFIED_CREATION = 7; static final int INDEX_UNIFIED_EXPIRY = 8; - static final String[] USER_IDS_PROJECTION = new String[] { - UserIds._ID, UserIds.USER_ID, UserIds.RANK, - UserIds.VERIFIED, UserIds.IS_PRIMARY, UserIds.IS_REVOKED - }; - static final String[] KEYS_PROJECTION = new String[] { Keys._ID, Keys.KEY_ID, Keys.RANK, Keys.ALGORITHM, Keys.KEY_SIZE, @@ -195,7 +190,7 @@ public class ViewKeyMainFragment extends Fragment implements } case LOADER_ID_USER_IDS: { Uri baseUri = UserIds.buildUserIdsUri(mDataUri); - return new CursorLoader(getActivity(), baseUri, USER_IDS_PROJECTION, null, null, null); + return new CursorLoader(getActivity(), baseUri, ViewKeyUserIdsAdapter.USER_IDS_PROJECTION, null, null, null); } case LOADER_ID_KEYS: { Uri baseUri = Keys.buildKeysUri(mDataUri); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java index 023d3d660..09137f745 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java @@ -23,6 +23,7 @@ import android.support.v4.widget.CursorAdapter; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.AdapterView; import android.widget.CheckBox; import android.widget.CompoundButton; import android.widget.ImageView; @@ -34,7 +35,7 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; import java.util.ArrayList; -public class ViewKeyUserIdsAdapter extends CursorAdapter { +public class ViewKeyUserIdsAdapter extends CursorAdapter implements AdapterView.OnItemClickListener { private LayoutInflater mInflater; private int mIndexUserId, mIndexRank; @@ -42,6 +43,11 @@ public class ViewKeyUserIdsAdapter extends CursorAdapter { private final ArrayList mCheckStates; + public static final String[] USER_IDS_PROJECTION = new String[] { + UserIds._ID, UserIds.USER_ID, UserIds.RANK, + UserIds.VERIFIED, UserIds.IS_PRIMARY, UserIds.IS_REVOKED + }; + public ViewKeyUserIdsAdapter(Context context, Cursor c, int flags, boolean showCheckBoxes) { super(context, c, flags); @@ -67,7 +73,7 @@ public class ViewKeyUserIdsAdapter extends CursorAdapter { for(int i = 0; i < count; i++) { newCursor.moveToPosition(i); int verified = newCursor.getInt(mVerifiedId); - mCheckStates.add(verified == 0); + mCheckStates.add(verified != Certs.VERIFIED_SECRET); } } } @@ -114,6 +120,7 @@ public class ViewKeyUserIdsAdapter extends CursorAdapter { vAddress.setText(userId[1]); if(cursor.getInt(mIsRevoked) > 0) { + vRank.setText(" "); vVerified.setImageResource(android.R.drawable.presence_away); } else { int verified = cursor.getInt(mVerifiedId); @@ -141,15 +148,17 @@ public class ViewKeyUserIdsAdapter extends CursorAdapter { mCheckStates.set(position, b); } }); - view.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - vCheckBox.toggle(); - } - }); + vCheckBox.setClickable(false); } + public void onItemClick(AdapterView adapter, View view, int position, long id) { + CheckBox box = ((CheckBox) view.findViewById(R.id.checkBox)); + if(box != null) { + box.toggle(); + } + } + public ArrayList getSelectedUserIds() { ArrayList result = new ArrayList(); for (int i = 0; i < mCheckStates.size(); i++) { diff --git a/OpenPGP-Keychain/src/main/res/layout/certify_key_activity.xml b/OpenPGP-Keychain/src/main/res/layout/certify_key_activity.xml index 6cd140739..3fa0468de 100644 --- a/OpenPGP-Keychain/src/main/res/layout/certify_key_activity.xml +++ b/OpenPGP-Keychain/src/main/res/layout/certify_key_activity.xml @@ -2,7 +2,7 @@ + android:layout_height="wrap_content" + android:descendantFocusability="blocksDescendants" /> + android:id="@+id/checkBox" + android:clickable="false" + android:focusable="false" /> + android:layout_gravity="center_vertical" + android:width="30sp" /> Date: Sun, 6 Apr 2014 03:15:59 +0200 Subject: [PATCH 25/30] certs: fix ViewCertActivity --- .../keychain/provider/KeychainContract.java | 3 ++ .../keychain/provider/KeychainProvider.java | 21 +++++++++--- .../keychain/ui/ViewCertActivity.java | 34 +++++++++---------- .../keychain/ui/ViewKeyCertsFragment.java | 23 +++++++++---- .../src/main/res/values/strings.xml | 13 +++---- 5 files changed, 58 insertions(+), 36 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java index 28d54d818..0eff929f3 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java @@ -273,6 +273,9 @@ public class KeychainContract { public static Uri buildCertsUri(String masterKeyId) { return CONTENT_URI.buildUpon().appendPath(masterKeyId).appendPath(PATH_CERTS).build(); } + public static Uri buildCertsSpecificUri(String masterKeyId, String rank, String certifier) { + return CONTENT_URI.buildUpon().appendPath(masterKeyId).appendPath(PATH_CERTS).appendPath(rank).appendPath(certifier).build(); + } public static Uri buildCertsUri(Uri uri) { return CONTENT_URI.buildUpon().appendPath(uri.getPathSegments().get(1)).appendPath(PATH_CERTS).build(); } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java index 903c27859..f684006b0 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -54,6 +54,7 @@ public class KeychainProvider extends ContentProvider { private static final int KEY_RING_PUBLIC = 203; private static final int KEY_RING_SECRET = 204; private static final int KEY_RING_CERTS = 205; + private static final int KEY_RING_CERTS_SPECIFIC = 206; private static final int API_APPS = 301; private static final int API_APPS_BY_PACKAGE_NAME = 303; @@ -63,8 +64,6 @@ public class KeychainProvider extends ContentProvider { private static final int KEY_RINGS_FIND_BY_EMAIL = 400; private static final int KEY_RINGS_FIND_BY_SUBKEY = 401; - private static final int CERTS_FIND_BY_CERTIFIER_ID = 501; - // private static final int DATA_STREAM = 501; protected UriMatcher mUriMatcher; @@ -119,6 +118,8 @@ public class KeychainProvider extends ContentProvider { * key_rings/_/user_ids * key_rings/_/public * key_rings/_/secret + * key_rings/_/certs + * key_rings/_/certs/_/_ *
*/ matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/*/" @@ -139,6 +140,9 @@ public class KeychainProvider extends ContentProvider { matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/*/" + KeychainContract.PATH_CERTS, KEY_RING_CERTS); + matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/*/" + + KeychainContract.PATH_CERTS + "/*/*", + KEY_RING_CERTS_SPECIFIC); /** * API apps @@ -436,6 +440,7 @@ public class KeychainProvider extends ContentProvider { } case KEY_RING_CERTS: + case KEY_RING_CERTS_SPECIFIC: { HashMap projectionMap = new HashMap(); projectionMap.put(Certs._ID, Tables.CERTS + ".oid AS " + Certs._ID); projectionMap.put(Certs.MASTER_KEY_ID, Tables.CERTS + "." + Certs.MASTER_KEY_ID); @@ -443,6 +448,7 @@ public class KeychainProvider extends ContentProvider { projectionMap.put(Certs.VERIFIED, Tables.CERTS + "." + Certs.VERIFIED); projectionMap.put(Certs.TYPE, Tables.CERTS + "." + Certs.TYPE); projectionMap.put(Certs.CREATION, Tables.CERTS + "." + Certs.CREATION); + projectionMap.put(Certs.EXPIRY, Tables.CERTS + "." + Certs.EXPIRY); projectionMap.put(Certs.KEY_ID_CERTIFIER, Tables.CERTS + "." + Certs.KEY_ID_CERTIFIER); projectionMap.put(Certs.USER_ID, Tables.USER_IDS + "." + UserIds.USER_ID); projectionMap.put(Certs.SIGNER_UID, "signer." + UserIds.USER_ID + " AS " + Certs.SIGNER_UID); @@ -450,7 +456,7 @@ public class KeychainProvider extends ContentProvider { qb.setTables(Tables.CERTS + " JOIN " + Tables.USER_IDS + " ON (" - + Tables.CERTS + "." + Certs.MASTER_KEY_ID + " = " + + Tables.CERTS + "." + Certs.MASTER_KEY_ID + " = " + Tables.USER_IDS + "." + UserIds.MASTER_KEY_ID + " AND " + Tables.CERTS + "." + Certs.RANK + " = " @@ -465,10 +471,17 @@ public class KeychainProvider extends ContentProvider { groupBy = Tables.CERTS + "." + Certs.RANK + ", " + Tables.CERTS + "." + Certs.KEY_ID_CERTIFIER; - qb.appendWhere(Tables.CERTS + "." + KeyRings.MASTER_KEY_ID + " = "); + qb.appendWhere(Tables.CERTS + "." + Certs.MASTER_KEY_ID + " = "); qb.appendWhereEscapeString(uri.getPathSegments().get(1)); + if(match == KEY_RING_CERTS_SPECIFIC) { + qb.appendWhere(" AND " + Tables.CERTS + "." + Certs.RANK + " = "); + qb.appendWhereEscapeString(uri.getPathSegments().get(3)); + qb.appendWhere(" AND " + Tables.CERTS + "." + Certs.KEY_ID_CERTIFIER+ " = "); + qb.appendWhereEscapeString(uri.getPathSegments().get(4)); + } break; + } case API_APPS: qb.setTables(Tables.API_APPS); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java index e5f2fb173..4e525cff8 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java @@ -59,16 +59,14 @@ public class ViewCertActivity extends ActionBarActivity Certs.EXPIRY, Certs.KEY_ID_CERTIFIER, Certs.SIGNER_UID, - Certs.TYPE }; - private static final int INDEX_MASTER_KEY_ID = 1; - private static final int INDEX_USER_ID = 2; - private static final int INDEX_TYPE = 3; - private static final int INDEX_CREATION = 4; - private static final int INDEX_EXPIRY = 5; - private static final int INDEX_KEY_ID_CERTIFIER = 6; - private static final int INDEX_UID_CERTIFIER = 7; - private static final int INDEX_KEY_TYPE = 8; + private static final int INDEX_MASTER_KEY_ID = 0; + private static final int INDEX_USER_ID = 1; + private static final int INDEX_TYPE = 2; + private static final int INDEX_CREATION = 3; + private static final int INDEX_EXPIRY = 4; + private static final int INDEX_KEY_ID_CERTIFIER = 5; + private static final int INDEX_SIGNER_UID = 6; private Uri mDataUri; @@ -130,7 +128,7 @@ public class ViewCertActivity extends ActionBarActivity String signerKey = "0x" + PgpKeyHelper.convertKeyIdToHex(mSignerKeyId); mSignerKey.setText(signerKey); - String signerUid = data.getString(INDEX_UID_CERTIFIER); + String signerUid = data.getString(INDEX_SIGNER_UID); if(signerUid != null) mSignerUid.setText(signerUid); else @@ -141,19 +139,21 @@ public class ViewCertActivity extends ActionBarActivity switch(data.getInt(INDEX_TYPE)) { case PGPSignature.DEFAULT_CERTIFICATION: - mType.setText(R.string.sig_type_default); break; + mType.setText(R.string.cert_default); break; case PGPSignature.NO_CERTIFICATION: - mType.setText(R.string.sig_type_none); break; + mType.setText(R.string.cert_none); break; case PGPSignature.CASUAL_CERTIFICATION: - mType.setText(R.string.sig_type_casual); break; + mType.setText(R.string.cert_casual); break; case PGPSignature.POSITIVE_CERTIFICATION: - mType.setText(R.string.sig_type_positive); break; + mType.setText(R.string.cert_positive); break; + case PGPSignature.CERTIFICATION_REVOCATION: + mType.setText(R.string.cert_revoke); break; } long expiry = data.getLong(INDEX_EXPIRY); - if(expiry == 0) - mExpiry.setText("never"); - else { + if(expiry == 0) { + mExpiry.setText(R.string.never); + } else { Date expiryDate = new Date(creationDate.getTime() + expiry * 1000); mExpiry.setText(DateFormat.getDateFormat(getApplicationContext()).format(expiryDate)); } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java index 325e9e179..a872443af 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java @@ -142,10 +142,16 @@ public class ViewKeyCertsFragment extends Fragment */ @Override public void onItemClick(AdapterView adapterView, View view, int position, long id) { - Intent viewIntent = null; - viewIntent = new Intent(getActivity(), ViewCertActivity.class); - viewIntent.setData(Certs.buildCertsUri(Long.toString(id))); - startActivity(viewIntent); + if(view.getTag(R.id.tag_mki) != null) { + long masterKeyId = (Long) view.getTag(R.id.tag_mki); + long rank = (Long) view.getTag(R.id.tag_rank); + long certifierId = (Long) view.getTag(R.id.tag_certifierId); + + Intent viewIntent = new Intent(getActivity(), ViewCertActivity.class); + viewIntent.setData(Certs.buildCertsSpecificUri( + Long.toString(masterKeyId), Long.toString(rank), Long.toString(certifierId))); + startActivity(viewIntent); + } } @Override @@ -161,8 +167,7 @@ public class ViewKeyCertsFragment extends Fragment */ private class CertListAdapter extends CursorAdapter implements StickyListHeadersAdapter { private LayoutInflater mInflater; - private int mIndexCertId; - private int mIndexUserId, mIndexRank; + private int mIndexMasterKeyId, mIndexUserId, mIndexRank; private int mIndexSignerKeyId, mIndexSignerUserId; private int mIndexVerified, mIndexType; @@ -189,7 +194,7 @@ public class ViewKeyCertsFragment extends Fragment private void initIndex(Cursor cursor) { if (cursor != null) { - mIndexCertId = cursor.getColumnIndexOrThrow(Certs.MASTER_KEY_ID); + mIndexMasterKeyId = cursor.getColumnIndexOrThrow(Certs.MASTER_KEY_ID); mIndexUserId = cursor.getColumnIndexOrThrow(Certs.USER_ID); mIndexRank = cursor.getColumnIndexOrThrow(Certs.RANK); mIndexType = cursor.getColumnIndexOrThrow(Certs.TYPE); @@ -231,6 +236,10 @@ public class ViewKeyCertsFragment extends Fragment wSignerUserId.setText(signerUserId); wSignerKeyId.setText(signerKeyId); + view.setTag(R.id.tag_mki, cursor.getLong(mIndexMasterKeyId)); + view.setTag(R.id.tag_rank, cursor.getLong(mIndexRank)); + view.setTag(R.id.tag_certifierId, cursor.getLong(mIndexSignerKeyId)); + } @Override diff --git a/OpenPGP-Keychain/src/main/res/values/strings.xml b/OpenPGP-Keychain/src/main/res/values/strings.xml index e678d8848..15693401f 100644 --- a/OpenPGP-Keychain/src/main/res/values/strings.xml +++ b/OpenPGP-Keychain/src/main/res/values/strings.xml @@ -486,14 +486,14 @@ available unavailable + + Write message here to encrypt and/or sign… + Enter ciphertext here to decrypt and/or verify… + Show unknown signatures Signer Certificate Details - default - none - casual - positive User ID Subkey Rank ]]> @@ -503,14 +503,11 @@ Show by known secret keys Show by known public keys Show all certificates - - - Write message here to encrypt and/or sign… - Enter ciphertext here to decrypt and/or verify… default none casual positive revoke + never From 456a634149d0eecb00f6d7e0053a71f1b19538b6 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sun, 6 Apr 2014 04:27:55 +0200 Subject: [PATCH 26/30] certs: ditch expiry, re-add data blob, improve ViewCertActivity GnuPG doesn't support expiry of user id certifications. The number of rings with an expiration subpacket in a cert out there is likely negligible. ViewCertActivity now verifies the key and displays a status. For revocation certs, the revocation reason is also shown. --- .../keychain/provider/KeychainContract.java | 2 +- .../keychain/provider/KeychainDatabase.java | 3 +- .../keychain/provider/KeychainProvider.java | 2 +- .../keychain/provider/ProviderHelper.java | 15 +--- .../keychain/ui/ViewCertActivity.java | 83 ++++++++++++++----- .../main/res/layout/view_cert_activity.xml | 57 +++++++++---- 6 files changed, 111 insertions(+), 51 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java index 0eff929f3..a029da478 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java @@ -62,7 +62,7 @@ public class KeychainContract { String TYPE = "type"; String VERIFIED = "verified"; String CREATION = "creation"; - String EXPIRY = "expiry"; + String DATA = "data"; } interface ApiAppsColumns { diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java index 2bd1c13a0..7fbfe1d60 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java @@ -114,7 +114,8 @@ public class KeychainDatabase extends SQLiteOpenHelper { + CertsColumns.TYPE + " INTEGER, " + CertsColumns.VERIFIED + " INTEGER, " + CertsColumns.CREATION + " INTEGER, " - + CertsColumns.EXPIRY + " INTEGER, " + + + CertsColumns.DATA + " BLOB, " + "PRIMARY KEY(" + CertsColumns.MASTER_KEY_ID + ", " + CertsColumns.RANK + ", " + CertsColumns.KEY_ID_CERTIFIER + "), " diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java index f684006b0..72cb53e76 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -448,8 +448,8 @@ public class KeychainProvider extends ContentProvider { projectionMap.put(Certs.VERIFIED, Tables.CERTS + "." + Certs.VERIFIED); projectionMap.put(Certs.TYPE, Tables.CERTS + "." + Certs.TYPE); projectionMap.put(Certs.CREATION, Tables.CERTS + "." + Certs.CREATION); - projectionMap.put(Certs.EXPIRY, Tables.CERTS + "." + Certs.EXPIRY); projectionMap.put(Certs.KEY_ID_CERTIFIER, Tables.CERTS + "." + Certs.KEY_ID_CERTIFIER); + projectionMap.put(Certs.DATA, Tables.CERTS + "." + Certs.DATA); projectionMap.put(Certs.USER_ID, Tables.USER_IDS + "." + UserIds.USER_ID); projectionMap.put(Certs.SIGNER_UID, "signer." + UserIds.USER_ID + " AS " + Certs.SIGNER_UID); qb.setProjectionMap(projectionMap); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index 78d61a521..bcea66498 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -246,10 +246,8 @@ public class ProviderHelper { try { // self signature if(certId == masterKeyId) { - cert.init( - new JcaPGPContentVerifierBuilderProvider().setProvider( - Constants.BOUNCY_CASTLE_PROVIDER_NAME), - masterKey); + cert.init(new JcaPGPContentVerifierBuilderProvider().setProvider( + Constants.BOUNCY_CASTLE_PROVIDER_NAME), masterKey); if(!cert.verifyCertification(userId, masterKey)) { // not verified?! dang! TODO notify user? this is kinda serious... Log.e(Constants.TAG, "Could not verify self signature for " + userId + "!"); @@ -267,8 +265,7 @@ public class ProviderHelper { // verify signatures from known private keys if(allKeyRings.containsKey(certId)) { // mark them as verified - cert.init( - new JcaPGPContentVerifierBuilderProvider().setProvider( + cert.init(new JcaPGPContentVerifierBuilderProvider().setProvider( Constants.BOUNCY_CASTLE_PROVIDER_NAME), allKeyRings.get(certId).getPublicKey()); if(cert.verifyCertification(userId, masterKey)) { @@ -423,12 +420,8 @@ public class ProviderHelper { values.put(Certs.KEY_ID_CERTIFIER, cert.getKeyID()); values.put(Certs.TYPE, cert.getSignatureType()); values.put(Certs.CREATION, cert.getCreationTime().getTime() / 1000); - if(cert.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.EXPIRE_TIME)) { - long ext = ((SignatureExpirationTime) cert.getHashedSubPackets().getSubpacket( - SignatureSubpacketTags.EXPIRE_TIME)).getTime(); - values.put(Certs.EXPIRY, cert.getCreationTime().getTime() / 1000 + ext); - } values.put(Certs.VERIFIED, verified); + values.put(Certs.DATA, cert.getEncoded()); Uri uri = Certs.buildCertsUri(Long.toString(masterKeyId)); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java index 4e525cff8..ac4194afc 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java @@ -30,18 +30,27 @@ import android.support.v7.app.ActionBarActivity; import android.text.format.DateFormat; import android.view.Menu; import android.view.MenuItem; +import android.view.View; import android.widget.TextView; +import org.spongycastle.bcpg.SignatureSubpacket; +import org.spongycastle.bcpg.SignatureSubpacketTags; +import org.spongycastle.bcpg.sig.RevocationReason; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPKeyRing; import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.pgp.PgpConversionHelper; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; +import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.util.Log; +import java.security.SignatureException; import java.util.Date; /** @@ -56,24 +65,25 @@ public class ViewCertActivity extends ActionBarActivity Certs.USER_ID, Certs.TYPE, Certs.CREATION, - Certs.EXPIRY, Certs.KEY_ID_CERTIFIER, Certs.SIGNER_UID, + Certs.DATA, }; private static final int INDEX_MASTER_KEY_ID = 0; private static final int INDEX_USER_ID = 1; private static final int INDEX_TYPE = 2; private static final int INDEX_CREATION = 3; - private static final int INDEX_EXPIRY = 4; - private static final int INDEX_KEY_ID_CERTIFIER = 5; - private static final int INDEX_SIGNER_UID = 6; + private static final int INDEX_KEY_ID_CERTIFIER = 4; + private static final int INDEX_SIGNER_UID = 5; + private static final int INDEX_DATA = 6; private Uri mDataUri; private long mSignerKeyId; - private TextView mSigneeKey, mSigneeUid, mRank, mAlgorithm, mType, mCreation, mExpiry; - private TextView mSignerKey, mSignerUid; + private TextView mSigneeKey, mSigneeUid, mAlgorithm, mType, mRReason, mCreation; + private TextView mSignerKey, mSignerUid, mStatus; + private View mRowReason; @Override protected void onCreate(Bundle savedInstanceState) { @@ -84,16 +94,19 @@ public class ViewCertActivity extends ActionBarActivity setContentView(R.layout.view_cert_activity); + mStatus = (TextView) findViewById(R.id.status); mSigneeKey = (TextView) findViewById(R.id.signee_key); mSigneeUid = (TextView) findViewById(R.id.signee_uid); mAlgorithm = (TextView) findViewById(R.id.algorithm); mType = (TextView) findViewById(R.id.signature_type); + mRReason = (TextView) findViewById(R.id.reason); mCreation = (TextView) findViewById(R.id.creation); - mExpiry = (TextView) findViewById(R.id.expiry); mSignerKey = (TextView) findViewById(R.id.signer_key_id); mSignerUid = (TextView) findViewById(R.id.signer_uid); + mRowReason = findViewById(R.id.row_reason); + mDataUri = getIntent().getData(); if (mDataUri == null) { Log.e(Constants.TAG, "Intent data missing. Should be Uri of key!"); @@ -134,9 +147,36 @@ public class ViewCertActivity extends ActionBarActivity else mSignerUid.setText(R.string.unknown_uid); - // String algorithmStr = PgpKeyHelper.getAlgorithmInfo(sig.getKeyAlgorithm(), 0); - // mAlgorithm.setText(algorithmStr); + PGPSignature sig = PgpConversionHelper.BytesToPGPSignature(data.getBlob(INDEX_DATA)); + PGPKeyRing signeeRing = ProviderHelper.getPGPKeyRing(this, + KeychainContract.KeyRingData.buildPublicKeyRingUri(Long.toString(data.getLong(INDEX_MASTER_KEY_ID)))); + PGPKeyRing signerRing = ProviderHelper.getPGPKeyRing(this, + KeychainContract.KeyRingData.buildPublicKeyRingUri(Long.toString(sig.getKeyID()))); + if(signerRing != null) try { + sig.init(new JcaPGPContentVerifierBuilderProvider().setProvider( + Constants.BOUNCY_CASTLE_PROVIDER_NAME), signeeRing.getPublicKey()); + if (sig.verifyCertification(signeeUid, signerRing.getPublicKey())) { + mStatus.setText("ok"); + mStatus.setTextColor(getResources().getColor(R.color.bbutton_success)); + } else { + mStatus.setText("failed!"); + mStatus.setTextColor(getResources().getColor(R.color.alert)); + } + } catch(SignatureException e) { + mStatus.setText("error!"); + mStatus.setTextColor(getResources().getColor(R.color.alert)); + } catch(PGPException e) { + mStatus.setText("error!"); + mStatus.setTextColor(getResources().getColor(R.color.alert)); + } else { + mStatus.setText("key unavailable"); + mStatus.setTextColor(getResources().getColor(R.color.black)); + } + String algorithmStr = PgpKeyHelper.getAlgorithmInfo(sig.getKeyAlgorithm(), 0); + mAlgorithm.setText(algorithmStr); + + mRowReason.setVisibility(View.GONE); switch(data.getInt(INDEX_TYPE)) { case PGPSignature.DEFAULT_CERTIFICATION: mType.setText(R.string.cert_default); break; @@ -146,16 +186,21 @@ public class ViewCertActivity extends ActionBarActivity mType.setText(R.string.cert_casual); break; case PGPSignature.POSITIVE_CERTIFICATION: mType.setText(R.string.cert_positive); break; - case PGPSignature.CERTIFICATION_REVOCATION: - mType.setText(R.string.cert_revoke); break; - } - - long expiry = data.getLong(INDEX_EXPIRY); - if(expiry == 0) { - mExpiry.setText(R.string.never); - } else { - Date expiryDate = new Date(creationDate.getTime() + expiry * 1000); - mExpiry.setText(DateFormat.getDateFormat(getApplicationContext()).format(expiryDate)); + case PGPSignature.CERTIFICATION_REVOCATION: { + mType.setText(R.string.cert_revoke); + if(sig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.REVOCATION_REASON)) { + SignatureSubpacket p = sig.getHashedSubPackets().getSubpacket( + SignatureSubpacketTags.REVOCATION_REASON); + // For some reason, this is missing in SignatureSubpacketInputStream:146 + if(!(p instanceof RevocationReason)) { + p = new RevocationReason(false, p.getData()); + } + String reason = ((RevocationReason) p).getRevocationDescription(); + mRReason.setText(reason); + mRowReason.setVisibility(View.VISIBLE); + } + break; + } } } } diff --git a/OpenPGP-Keychain/src/main/res/layout/view_cert_activity.xml b/OpenPGP-Keychain/src/main/res/layout/view_cert_activity.xml index 3c6c09429..95b8ffc8d 100644 --- a/OpenPGP-Keychain/src/main/res/layout/view_cert_activity.xml +++ b/OpenPGP-Keychain/src/main/res/layout/view_cert_activity.xml @@ -14,6 +14,26 @@ android:paddingLeft="16dp" android:paddingRight="16dp"> + + + + + + + + + + + + + + - - - - - - - Date: Sun, 6 Apr 2014 05:07:14 +0200 Subject: [PATCH 27/30] certs: forgot to add ids file --- OpenPGP-Keychain/src/main/res/values/ids.xml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 OpenPGP-Keychain/src/main/res/values/ids.xml diff --git a/OpenPGP-Keychain/src/main/res/values/ids.xml b/OpenPGP-Keychain/src/main/res/values/ids.xml new file mode 100644 index 000000000..2004c0397 --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/values/ids.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file From bd8dc05b1c013581e6fca33ece1c8dc5649915da Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sun, 6 Apr 2014 05:29:19 +0200 Subject: [PATCH 28/30] certs: display green dot for certified keys in KeyListFragment --- .../keychain/provider/KeychainContract.java | 1 + .../keychain/provider/KeychainProvider.java | 11 ++++++++++- .../keychain/ui/KeyListFragment.java | 19 ++++++++++++++++--- .../src/main/res/layout/key_list_item.xml | 8 ++++++++ 4 files changed, 35 insertions(+), 4 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java index a029da478..cad40d4a0 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java @@ -110,6 +110,7 @@ public class KeychainContract { public static class KeyRings implements BaseColumns, KeysColumns, UserIdsColumns { public static final String MASTER_KEY_ID = KeysColumns.MASTER_KEY_ID; public static final String IS_REVOKED = KeysColumns.IS_REVOKED; + public static final String VERIFIED = CertsColumns.VERIFIED; public static final String HAS_SECRET = "has_secret"; public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java index 72cb53e76..9b9e4991d 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -257,11 +257,12 @@ public class KeychainProvider extends ContentProvider { projectionMap.put(KeyRings.CAN_CERTIFY, Keys.CAN_CERTIFY); projectionMap.put(KeyRings.CAN_ENCRYPT, Keys.CAN_ENCRYPT); projectionMap.put(KeyRings.CAN_SIGN, Keys.CAN_SIGN); - projectionMap.put(KeyRings.CREATION, Keys.CREATION); + projectionMap.put(KeyRings.CREATION, Tables.KEYS + "." + Keys.CREATION); projectionMap.put(KeyRings.EXPIRY, Keys.EXPIRY); projectionMap.put(KeyRings.ALGORITHM, Keys.ALGORITHM); projectionMap.put(KeyRings.FINGERPRINT, Keys.FINGERPRINT); projectionMap.put(KeyRings.USER_ID, UserIds.USER_ID); + projectionMap.put(KeyRings.VERIFIED, KeyRings.VERIFIED); projectionMap.put(KeyRings.HAS_SECRET, "(" + Tables.KEY_RINGS_SECRET + "." + KeyRings.MASTER_KEY_ID + " IS NOT NULL) AS " + KeyRings.HAS_SECRET); qb.setProjectionMap(projectionMap); @@ -276,9 +277,17 @@ public class KeychainProvider extends ContentProvider { + Tables.KEYS + "." + Keys.MASTER_KEY_ID + " = " + Tables.KEY_RINGS_SECRET + "." + KeyRings.MASTER_KEY_ID + + ") LEFT JOIN " + Tables.CERTS + " ON (" + + Tables.KEYS + "." + Keys.MASTER_KEY_ID + + " = " + + Tables.CERTS + "." + KeyRings.MASTER_KEY_ID + + " AND " + Tables.CERTS + "." + Certs.VERIFIED + + " = " + Certs.VERIFIED_SECRET + ")" ); qb.appendWhere(Tables.KEYS + "." + Keys.RANK + " = 0"); + // in case there are multiple verifying certificates + groupBy = Tables.KEYS + "." + Keys.MASTER_KEY_ID; switch(match) { case KEY_RING_UNIFIED: { diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java index 38fce0f0a..3e2c96464 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java @@ -50,6 +50,7 @@ import android.widget.AbsListView.MultiChoiceModeListener; import android.widget.AdapterView; import android.widget.Button; import android.widget.FrameLayout; +import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; import com.beardedhen.androidbootstrap.BootstrapButton; @@ -251,13 +252,15 @@ public class KeyListFragment extends Fragment KeyRings.MASTER_KEY_ID, KeyRings.USER_ID, KeyRings.IS_REVOKED, + KeyRings.VERIFIED, KeyRings.HAS_SECRET }; static final int INDEX_MASTER_KEY_ID = 1; static final int INDEX_USER_ID = 2; static final int INDEX_IS_REVOKED = 3; - static final int INDEX_HAS_SECRET = 4; + static final int INDEX_VERIFIED = 4; + static final int INDEX_HAS_SECRET = 5; @Override public Loader onCreateLoader(int id, Bundle args) { @@ -491,12 +494,14 @@ public class KeyListFragment extends Fragment FrameLayout statusLayout = (FrameLayout) view.findViewById(R.id.status_layout); Button button = (Button) view.findViewById(R.id.edit); TextView revoked = (TextView) view.findViewById(R.id.revoked); + ImageView verified = (ImageView) view.findViewById(R.id.verified); if (cursor.getInt(KeyListFragment.INDEX_HAS_SECRET) != 0) { // this is a secret key - show the edit button statusDivider.setVisibility(View.VISIBLE); statusLayout.setVisibility(View.VISIBLE); revoked.setVisibility(View.GONE); + verified.setVisibility(View.GONE); button.setVisibility(View.VISIBLE); final long id = cursor.getLong(INDEX_MASTER_KEY_ID); @@ -514,8 +519,16 @@ public class KeyListFragment extends Fragment button.setVisibility(View.GONE); boolean isRevoked = cursor.getInt(INDEX_IS_REVOKED) > 0; - statusLayout.setVisibility(isRevoked ? View.VISIBLE : View.GONE); - revoked.setVisibility(isRevoked ? View.VISIBLE : View.GONE); + if(isRevoked) { + statusLayout.setVisibility(isRevoked ? View.VISIBLE : View.GONE); + revoked.setVisibility(isRevoked ? View.VISIBLE : View.GONE); + verified.setVisibility(View.GONE); + } else { + boolean isVerified = cursor.getInt(INDEX_VERIFIED) > 0; + statusLayout.setVisibility(isVerified ? View.VISIBLE : View.GONE); + revoked.setVisibility(View.GONE); + verified.setVisibility(isVerified ? View.VISIBLE : View.GONE); + } } } diff --git a/OpenPGP-Keychain/src/main/res/layout/key_list_item.xml b/OpenPGP-Keychain/src/main/res/layout/key_list_item.xml index 0abae8bbb..84ad9f9b5 100644 --- a/OpenPGP-Keychain/src/main/res/layout/key_list_item.xml +++ b/OpenPGP-Keychain/src/main/res/layout/key_list_item.xml @@ -73,6 +73,14 @@ android:text="@string/revoked" android:textColor="#e00" android:layout_gravity="center" /> + + From 8b6c31ba46f75bdb941dc95a57ef5388afdf411d Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sun, 6 Apr 2014 05:29:46 +0200 Subject: [PATCH 29/30] certs: change migration routine so certifications work --- .../keychain/provider/KeychainDatabase.java | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java index 7fbfe1d60..51f8d8e8e 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java @@ -194,8 +194,8 @@ public class KeychainDatabase extends SQLiteOpenHelper { /** This method tries to import data from a provided database. * * The sole assumptions made on this db are that there is a key_rings table - * with a key_ring_data and a type column, the latter of which should be bigger - * for secret keys. + * with a key_ring_data, a master_key_id and a type column, the latter of + * which should be 1 for secret keys and 0 for public keys. */ public void checkAndImportApg(Context context) { @@ -240,8 +240,32 @@ public class KeychainDatabase extends SQLiteOpenHelper { Log.d(Constants.TAG, "Ok."); } - Cursor c = db.rawQuery("SELECT key_ring_data FROM key_rings ORDER BY type ASC", null); + Cursor c = null; try { + // we insert in two steps: first, all public keys that have secret keys + c = db.rawQuery("SELECT key_ring_data FROM key_rings WHERE type = 1 OR EXISTS (" + + " SELECT 1 FROM key_rings d2 WHERE key_rings.master_key_id = d2.master_key_id" + + " AND d2.type = 1) ORDER BY type ASC", null); + Log.d(Constants.TAG, "Importing " + c.getCount() + " secret keyrings from apg.db..."); + for(int i = 0; i < c.getCount(); i++) { + c.moveToPosition(i); + byte[] data = c.getBlob(0); + PGPKeyRing ring = PgpConversionHelper.BytesToPGPKeyRing(data); + if(ring instanceof PGPPublicKeyRing) + ProviderHelper.saveKeyRing(context, (PGPPublicKeyRing) ring); + else if(ring instanceof PGPSecretKeyRing) + ProviderHelper.saveKeyRing(context, (PGPSecretKeyRing) ring); + else { + Log.e(Constants.TAG, "Unknown blob data type!"); + } + } + + // afterwards, insert all keys, starting with public keys that have secret keys, then + // secret keys, then all others. this order is necessary to ensure all certifications + // are recognized properly. + c = db.rawQuery("SELECT key_ring_data FROM key_rings ORDER BY (type = 0 AND EXISTS (" + + " SELECT 1 FROM key_rings d2 WHERE key_rings.master_key_id = d2.master_key_id AND" + + " d2.type = 1)) DESC, type DESC", null); // import from old database Log.d(Constants.TAG, "Importing " + c.getCount() + " keyrings from apg.db..."); for(int i = 0; i < c.getCount(); i++) { From d5842940b503c4e21714fc03a764df486ccc28e7 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sun, 6 Apr 2014 05:34:34 +0200 Subject: [PATCH 30/30] certs: remove unused menu item --- .../org/sufficientlysecure/keychain/ui/ViewCertActivity.java | 3 --- OpenPGP-Keychain/src/main/res/menu/view_cert.xml | 4 ---- 2 files changed, 7 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java index ac4194afc..b130519ba 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java @@ -220,9 +220,6 @@ public class ViewCertActivity extends ActionBarActivity @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { - case R.id.menu_view_cert_verify: - // TODO verification routine - return true; case R.id.menu_view_cert_view_signer: // can't do this before the data is initialized Intent viewIntent = null; diff --git a/OpenPGP-Keychain/src/main/res/menu/view_cert.xml b/OpenPGP-Keychain/src/main/res/menu/view_cert.xml index dbdb2bfbe..8c8e455c7 100644 --- a/OpenPGP-Keychain/src/main/res/menu/view_cert.xml +++ b/OpenPGP-Keychain/src/main/res/menu/view_cert.xml @@ -2,10 +2,6 @@ -