diff options
Diffstat (limited to 'OpenPGP-Keychain/src/main')
9 files changed, 448 insertions, 10 deletions
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 @@ -93,6 +93,15 @@ android:value=".ui.KeyListActivity" /> </activity> <activity + android:name=".ui.ViewCertActivity" + android:configChanges="orientation|screenSize|keyboardHidden|keyboard" + android:label="View Certificate Details" + android:parentActivityName=".ui.ViewKeyActivity"> + <meta-data + android:name="android.support.PARENT_ACTIVITY" + android:value=".ui.ViewKeyActivity" /> + </activity> + <activity android:name=".ui.SelectPublicKeyActivity" android:configChanges="orientation|screenSize|keyboardHidden|keyboard" android:label="@string/title_select_recipients" diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConversionHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConversionHelper.java index 20d446824..ca97cbd9f 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConversionHelper.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConversionHelper.java @@ -26,6 +26,8 @@ import org.spongycastle.openpgp.PGPKeyRing; import org.spongycastle.openpgp.PGPObjectFactory; import org.spongycastle.openpgp.PGPSecretKey; import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.PGPSignatureList; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.util.Log; @@ -104,6 +106,28 @@ public class PgpConversionHelper { } /** + * Convert from byte[] to PGPSignature + * + * @param sigBytes + * @return + */ + public static PGPSignature BytesToPGPSignature(byte[] sigBytes) { + PGPObjectFactory factory = new PGPObjectFactory(sigBytes); + PGPSignatureList signatures = null; + try { + if ((signatures = (PGPSignatureList) factory.nextObject()) == null || signatures.isEmpty()) { + Log.e(Constants.TAG, "No signatures given!"); + return null; + } + } catch (IOException e) { + Log.e(Constants.TAG, "Error while converting to PGPSignature!", e); + return null; + } + + return signatures.get(0); + } + + /** * Convert from ArrayList<PGPSecretKey> to byte[] * * @param keys 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<String, String> pmap2 = new HashMap<String, String>(); 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 <dominik@dominikschuermann.de> + * 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<Cursor> { + + // 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<Cursor> 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<Cursor> 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<Cursor> 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<Cursor> { + implements LoaderManager.LoaderCallbacks<Cursor>, 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<Cursor> 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<Cursor>{ 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 @@ +<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <!-- focusable and related properties to workaround http://stackoverflow.com/q/16182331--> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:focusable="true" + android:focusableInTouchMode="true" + android:descendantFocusability="beforeDescendants" + android:orientation="vertical" + android:paddingLeft="16dp" + android:paddingRight="16dp"> + + <TextView + style="@style/SectionHeader" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginBottom="4dp" + android:layout_marginTop="14dp" + android:text="@string/section_cert" /> + + <TableLayout + android:layout_width="wrap_content" + android:layout_height="0dp" + android:layout_weight="1" + android:stretchColumns="1"> + + <TableRow> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="top" + android:paddingRight="10dip" + android:text="@string/label_key_id" /> + + <TextView + android:id="@+id/signee_key" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:paddingRight="5dip" /> + </TableRow> + + <TableRow> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="top" + android:paddingRight="10dip" + android:text="@string/label_user_id" /> + + <TextView + android:id="@+id/signee_uid" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:paddingRight="5dip" /> + </TableRow> + + <TableRow + android:layout_width="fill_parent" + android:layout_height="fill_parent"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="top" + android:paddingRight="10dip" + android:text="@string/label_subkey_rank" /> + + <TextView + android:id="@+id/subkey_rank" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:paddingRight="5dip" /> + </TableRow> + + <TableRow + android:layout_width="fill_parent" + android:layout_height="fill_parent"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="top" + android:paddingRight="10dip" + android:text="@string/label_algorithm" /> + + <TextView + android:id="@+id/algorithm" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:paddingRight="5dip" /> + </TableRow> + + <TableRow + android:layout_width="fill_parent" + android:layout_height="fill_parent"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="top" + android:paddingRight="10dip" + android:text="Type" /> + + <TextView + android:id="@+id/signature_type" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:paddingRight="5dip" /> + </TableRow> + + <TableRow> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="top" + android:paddingRight="10dip" + android:text="Creation" /> + + <TextView + android:id="@+id/creation" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:paddingRight="5dip" /> + </TableRow> + + <TableRow + android:layout_width="fill_parent" + android:layout_height="fill_parent"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="top" + android:paddingRight="10dip" + android:text="Expires" /> + + <TextView + android:id="@+id/expiry" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:paddingRight="5dip" /> + </TableRow> + + </TableLayout> + + <TextView + style="@style/SectionHeader" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginBottom="4dp" + android:layout_marginTop="14dp" + android:text="@string/section_signer_id" /> + + <TableLayout + android:layout_width="wrap_content" + android:layout_height="0dp" + android:layout_weight="1" + android:stretchColumns="1"> + + <TableRow> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" + android:paddingRight="10dip" + android:text="@string/label_key_id" /> + + <TextView + android:id="@+id/signer_key_id" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingRight="5dip" + android:text="" + android:typeface="monospace" /> + </TableRow> + + <TableRow> + + <TextView + android:id="@+id/label_algorithm" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" + android:paddingRight="10dip" + android:text="@string/label_email" /> + + <TextView + android:id="@+id/signer_uid" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingRight="5dip" + android:text="" /> + </TableRow> + + </TableLayout> + + </LinearLayout> + +</ScrollView> 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 @@ <string name="secret_key_yes">available</string> <string name="secret_key_no">unavailable</string> <string name="show_unknown_signatures">Show unknown signatures</string> + <string name="section_signer_id">Signer</string> + <string name="section_cert">Certificate Details</string> + <string name="sig_type_default">default</string> + <string name="sig_type_none">none</string> + <string name="sig_type_casual">casual</string> + <string name="sig_type_positive">positive</string> + <string name="label_user_id">User ID</string> + <string name="label_subkey_rank">Subkey Rank</string> + <string name="unknown_uid"><![CDATA[<unknown>]]></string> </resources> |