mirror of
https://github.com/moparisthebest/open-keychain
synced 2024-11-30 04:22:18 -05:00
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.
This commit is contained in:
parent
f01a96f56e
commit
456a634149
@ -62,7 +62,7 @@ public class KeychainContract {
|
|||||||
String TYPE = "type";
|
String TYPE = "type";
|
||||||
String VERIFIED = "verified";
|
String VERIFIED = "verified";
|
||||||
String CREATION = "creation";
|
String CREATION = "creation";
|
||||||
String EXPIRY = "expiry";
|
String DATA = "data";
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ApiAppsColumns {
|
interface ApiAppsColumns {
|
||||||
|
@ -114,7 +114,8 @@ public class KeychainDatabase extends SQLiteOpenHelper {
|
|||||||
+ CertsColumns.TYPE + " INTEGER, "
|
+ CertsColumns.TYPE + " INTEGER, "
|
||||||
+ CertsColumns.VERIFIED + " INTEGER, "
|
+ CertsColumns.VERIFIED + " INTEGER, "
|
||||||
+ CertsColumns.CREATION + " INTEGER, "
|
+ CertsColumns.CREATION + " INTEGER, "
|
||||||
+ CertsColumns.EXPIRY + " INTEGER, "
|
|
||||||
|
+ CertsColumns.DATA + " BLOB, "
|
||||||
|
|
||||||
+ "PRIMARY KEY(" + CertsColumns.MASTER_KEY_ID + ", " + CertsColumns.RANK + ", "
|
+ "PRIMARY KEY(" + CertsColumns.MASTER_KEY_ID + ", " + CertsColumns.RANK + ", "
|
||||||
+ CertsColumns.KEY_ID_CERTIFIER + "), "
|
+ CertsColumns.KEY_ID_CERTIFIER + "), "
|
||||||
|
@ -448,8 +448,8 @@ public class KeychainProvider extends ContentProvider {
|
|||||||
projectionMap.put(Certs.VERIFIED, Tables.CERTS + "." + Certs.VERIFIED);
|
projectionMap.put(Certs.VERIFIED, Tables.CERTS + "." + Certs.VERIFIED);
|
||||||
projectionMap.put(Certs.TYPE, Tables.CERTS + "." + Certs.TYPE);
|
projectionMap.put(Certs.TYPE, Tables.CERTS + "." + Certs.TYPE);
|
||||||
projectionMap.put(Certs.CREATION, Tables.CERTS + "." + Certs.CREATION);
|
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.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.USER_ID, Tables.USER_IDS + "." + UserIds.USER_ID);
|
||||||
projectionMap.put(Certs.SIGNER_UID, "signer." + UserIds.USER_ID + " AS " + Certs.SIGNER_UID);
|
projectionMap.put(Certs.SIGNER_UID, "signer." + UserIds.USER_ID + " AS " + Certs.SIGNER_UID);
|
||||||
qb.setProjectionMap(projectionMap);
|
qb.setProjectionMap(projectionMap);
|
||||||
|
@ -246,10 +246,8 @@ public class ProviderHelper {
|
|||||||
try {
|
try {
|
||||||
// self signature
|
// self signature
|
||||||
if(certId == masterKeyId) {
|
if(certId == masterKeyId) {
|
||||||
cert.init(
|
cert.init(new JcaPGPContentVerifierBuilderProvider().setProvider(
|
||||||
new JcaPGPContentVerifierBuilderProvider().setProvider(
|
Constants.BOUNCY_CASTLE_PROVIDER_NAME), masterKey);
|
||||||
Constants.BOUNCY_CASTLE_PROVIDER_NAME),
|
|
||||||
masterKey);
|
|
||||||
if(!cert.verifyCertification(userId, masterKey)) {
|
if(!cert.verifyCertification(userId, masterKey)) {
|
||||||
// not verified?! dang! TODO notify user? this is kinda serious...
|
// not verified?! dang! TODO notify user? this is kinda serious...
|
||||||
Log.e(Constants.TAG, "Could not verify self signature for " + userId + "!");
|
Log.e(Constants.TAG, "Could not verify self signature for " + userId + "!");
|
||||||
@ -267,8 +265,7 @@ public class ProviderHelper {
|
|||||||
// verify signatures from known private keys
|
// verify signatures from known private keys
|
||||||
if(allKeyRings.containsKey(certId)) {
|
if(allKeyRings.containsKey(certId)) {
|
||||||
// mark them as verified
|
// mark them as verified
|
||||||
cert.init(
|
cert.init(new JcaPGPContentVerifierBuilderProvider().setProvider(
|
||||||
new JcaPGPContentVerifierBuilderProvider().setProvider(
|
|
||||||
Constants.BOUNCY_CASTLE_PROVIDER_NAME),
|
Constants.BOUNCY_CASTLE_PROVIDER_NAME),
|
||||||
allKeyRings.get(certId).getPublicKey());
|
allKeyRings.get(certId).getPublicKey());
|
||||||
if(cert.verifyCertification(userId, masterKey)) {
|
if(cert.verifyCertification(userId, masterKey)) {
|
||||||
@ -423,12 +420,8 @@ public class ProviderHelper {
|
|||||||
values.put(Certs.KEY_ID_CERTIFIER, cert.getKeyID());
|
values.put(Certs.KEY_ID_CERTIFIER, cert.getKeyID());
|
||||||
values.put(Certs.TYPE, cert.getSignatureType());
|
values.put(Certs.TYPE, cert.getSignatureType());
|
||||||
values.put(Certs.CREATION, cert.getCreationTime().getTime() / 1000);
|
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.VERIFIED, verified);
|
||||||
|
values.put(Certs.DATA, cert.getEncoded());
|
||||||
|
|
||||||
Uri uri = Certs.buildCertsUri(Long.toString(masterKeyId));
|
Uri uri = Certs.buildCertsUri(Long.toString(masterKeyId));
|
||||||
|
|
||||||
|
@ -30,18 +30,27 @@ import android.support.v7.app.ActionBarActivity;
|
|||||||
import android.text.format.DateFormat;
|
import android.text.format.DateFormat;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
import android.widget.TextView;
|
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.PGPSignature;
|
||||||
|
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
|
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
|
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.KeyRings;
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
|
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
|
||||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
|
||||||
|
import java.security.SignatureException;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -56,24 +65,25 @@ public class ViewCertActivity extends ActionBarActivity
|
|||||||
Certs.USER_ID,
|
Certs.USER_ID,
|
||||||
Certs.TYPE,
|
Certs.TYPE,
|
||||||
Certs.CREATION,
|
Certs.CREATION,
|
||||||
Certs.EXPIRY,
|
|
||||||
Certs.KEY_ID_CERTIFIER,
|
Certs.KEY_ID_CERTIFIER,
|
||||||
Certs.SIGNER_UID,
|
Certs.SIGNER_UID,
|
||||||
|
Certs.DATA,
|
||||||
};
|
};
|
||||||
private static final int INDEX_MASTER_KEY_ID = 0;
|
private static final int INDEX_MASTER_KEY_ID = 0;
|
||||||
private static final int INDEX_USER_ID = 1;
|
private static final int INDEX_USER_ID = 1;
|
||||||
private static final int INDEX_TYPE = 2;
|
private static final int INDEX_TYPE = 2;
|
||||||
private static final int INDEX_CREATION = 3;
|
private static final int INDEX_CREATION = 3;
|
||||||
private static final int INDEX_EXPIRY = 4;
|
private static final int INDEX_KEY_ID_CERTIFIER = 4;
|
||||||
private static final int INDEX_KEY_ID_CERTIFIER = 5;
|
private static final int INDEX_SIGNER_UID = 5;
|
||||||
private static final int INDEX_SIGNER_UID = 6;
|
private static final int INDEX_DATA = 6;
|
||||||
|
|
||||||
private Uri mDataUri;
|
private Uri mDataUri;
|
||||||
|
|
||||||
private long mSignerKeyId;
|
private long mSignerKeyId;
|
||||||
|
|
||||||
private TextView mSigneeKey, mSigneeUid, mRank, mAlgorithm, mType, mCreation, mExpiry;
|
private TextView mSigneeKey, mSigneeUid, mAlgorithm, mType, mRReason, mCreation;
|
||||||
private TextView mSignerKey, mSignerUid;
|
private TextView mSignerKey, mSignerUid, mStatus;
|
||||||
|
private View mRowReason;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
@ -84,16 +94,19 @@ public class ViewCertActivity extends ActionBarActivity
|
|||||||
|
|
||||||
setContentView(R.layout.view_cert_activity);
|
setContentView(R.layout.view_cert_activity);
|
||||||
|
|
||||||
|
mStatus = (TextView) findViewById(R.id.status);
|
||||||
mSigneeKey = (TextView) findViewById(R.id.signee_key);
|
mSigneeKey = (TextView) findViewById(R.id.signee_key);
|
||||||
mSigneeUid = (TextView) findViewById(R.id.signee_uid);
|
mSigneeUid = (TextView) findViewById(R.id.signee_uid);
|
||||||
mAlgorithm = (TextView) findViewById(R.id.algorithm);
|
mAlgorithm = (TextView) findViewById(R.id.algorithm);
|
||||||
mType = (TextView) findViewById(R.id.signature_type);
|
mType = (TextView) findViewById(R.id.signature_type);
|
||||||
|
mRReason = (TextView) findViewById(R.id.reason);
|
||||||
mCreation = (TextView) findViewById(R.id.creation);
|
mCreation = (TextView) findViewById(R.id.creation);
|
||||||
mExpiry = (TextView) findViewById(R.id.expiry);
|
|
||||||
|
|
||||||
mSignerKey = (TextView) findViewById(R.id.signer_key_id);
|
mSignerKey = (TextView) findViewById(R.id.signer_key_id);
|
||||||
mSignerUid = (TextView) findViewById(R.id.signer_uid);
|
mSignerUid = (TextView) findViewById(R.id.signer_uid);
|
||||||
|
|
||||||
|
mRowReason = findViewById(R.id.row_reason);
|
||||||
|
|
||||||
mDataUri = getIntent().getData();
|
mDataUri = getIntent().getData();
|
||||||
if (mDataUri == null) {
|
if (mDataUri == null) {
|
||||||
Log.e(Constants.TAG, "Intent data missing. Should be Uri of key!");
|
Log.e(Constants.TAG, "Intent data missing. Should be Uri of key!");
|
||||||
@ -134,9 +147,36 @@ public class ViewCertActivity extends ActionBarActivity
|
|||||||
else
|
else
|
||||||
mSignerUid.setText(R.string.unknown_uid);
|
mSignerUid.setText(R.string.unknown_uid);
|
||||||
|
|
||||||
// String algorithmStr = PgpKeyHelper.getAlgorithmInfo(sig.getKeyAlgorithm(), 0);
|
PGPSignature sig = PgpConversionHelper.BytesToPGPSignature(data.getBlob(INDEX_DATA));
|
||||||
// mAlgorithm.setText(algorithmStr);
|
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)) {
|
switch(data.getInt(INDEX_TYPE)) {
|
||||||
case PGPSignature.DEFAULT_CERTIFICATION:
|
case PGPSignature.DEFAULT_CERTIFICATION:
|
||||||
mType.setText(R.string.cert_default); break;
|
mType.setText(R.string.cert_default); break;
|
||||||
@ -146,16 +186,21 @@ public class ViewCertActivity extends ActionBarActivity
|
|||||||
mType.setText(R.string.cert_casual); break;
|
mType.setText(R.string.cert_casual); break;
|
||||||
case PGPSignature.POSITIVE_CERTIFICATION:
|
case PGPSignature.POSITIVE_CERTIFICATION:
|
||||||
mType.setText(R.string.cert_positive); break;
|
mType.setText(R.string.cert_positive); break;
|
||||||
case PGPSignature.CERTIFICATION_REVOCATION:
|
case PGPSignature.CERTIFICATION_REVOCATION: {
|
||||||
mType.setText(R.string.cert_revoke); break;
|
mType.setText(R.string.cert_revoke);
|
||||||
}
|
if(sig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.REVOCATION_REASON)) {
|
||||||
|
SignatureSubpacket p = sig.getHashedSubPackets().getSubpacket(
|
||||||
long expiry = data.getLong(INDEX_EXPIRY);
|
SignatureSubpacketTags.REVOCATION_REASON);
|
||||||
if(expiry == 0) {
|
// For some reason, this is missing in SignatureSubpacketInputStream:146
|
||||||
mExpiry.setText(R.string.never);
|
if(!(p instanceof RevocationReason)) {
|
||||||
} else {
|
p = new RevocationReason(false, p.getData());
|
||||||
Date expiryDate = new Date(creationDate.getTime() + expiry * 1000);
|
}
|
||||||
mExpiry.setText(DateFormat.getDateFormat(getApplicationContext()).format(expiryDate));
|
String reason = ((RevocationReason) p).getRevocationDescription();
|
||||||
|
mRReason.setText(reason);
|
||||||
|
mRowReason.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,26 @@
|
|||||||
android:paddingLeft="16dp"
|
android:paddingLeft="16dp"
|
||||||
android:paddingRight="16dp">
|
android:paddingRight="16dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
|
android:layout_marginTop="14dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Verification Status" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="ok"
|
||||||
|
android:id="@+id/status"
|
||||||
|
android:layout_marginLeft="30dp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
style="@style/SectionHeader"
|
style="@style/SectionHeader"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
@ -96,6 +116,25 @@
|
|||||||
android:paddingRight="5dip" />
|
android:paddingRight="5dip" />
|
||||||
</TableRow>
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="fill_parent"
|
||||||
|
android:id="@+id/row_reason">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="top"
|
||||||
|
android:paddingRight="10dip"
|
||||||
|
android:text="Revocation Reason" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/reason"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingRight="5dip" />
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
<TableRow>
|
<TableRow>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@ -112,24 +151,6 @@
|
|||||||
android:paddingRight="5dip" />
|
android:paddingRight="5dip" />
|
||||||
</TableRow>
|
</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>
|
</TableLayout>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
|
Loading…
Reference in New Issue
Block a user