From 456a634149d0eecb00f6d7e0053a71f1b19538b6 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sun, 6 Apr 2014 04:27:55 +0200 Subject: 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 +++++++++++++++++----- 5 files changed, 72 insertions(+), 33 deletions(-) (limited to 'OpenPGP-Keychain/src/main/java/org') 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; + } } } } -- cgit v1.2.3