From 84d5ca7cd9e9b4724756065ed5895eea92ff2d66 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Fri, 25 Nov 2016 20:44:14 +0100 Subject: [PATCH] wip: trust id logic --- .../pgp/PgpDecryptVerifyInputParcel.java | 2 - .../keychain/provider/KeychainProvider.java | 22 +++++ .../TrustIdentityDataAccessObject.java | 31 ++++++ .../keychain/remote/OpenPgpService.java | 99 +++++++++++++++++-- 4 files changed, 146 insertions(+), 8 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyInputParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyInputParcel.java index 767bf1e9d..ae538637a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyInputParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyInputParcel.java @@ -19,8 +19,6 @@ package org.sufficientlysecure.keychain.pgp; -import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.List; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java index 2e8177d88..672a00134 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -35,6 +35,7 @@ import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAllowedKeys; import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps; +import org.sufficientlysecure.keychain.provider.KeychainContract.ApiTrustIdentity; import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; @@ -929,6 +930,27 @@ public class KeychainProvider extends ContentProvider { db.update(Tables.UPDATED_KEYS, values, null, null); break; } + case TRUST_IDS_BY_PACKAGE_NAME_AND_TRUST_ID: { + Long masterKeyId = values.getAsLong(ApiTrustIdentity.MASTER_KEY_ID); + long updateTime = values.getAsLong(KeychainContract.ApiTrustIdentity.LAST_UPDATED); + if (masterKeyId == null) { + throw new IllegalArgumentException("master_key_id must be a non-null value!"); + } + + ContentValues actualValues = new ContentValues(); + String packageName = uri.getPathSegments().get(2); + actualValues.put(ApiTrustIdentity.PACKAGE_NAME, packageName); + actualValues.put(ApiTrustIdentity.IDENTIFIER, uri.getLastPathSegment()); + actualValues.put(ApiTrustIdentity.MASTER_KEY_ID, masterKeyId); + actualValues.put(ApiTrustIdentity.LAST_UPDATED, updateTime); + + try { + db.replace(Tables.API_TRUST_IDENTITIES, null, actualValues); + } finally { + db.close(); + } + break; + } default: { throw new UnsupportedOperationException("Unknown uri: " + uri); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/TrustIdentityDataAccessObject.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/TrustIdentityDataAccessObject.java index 9eaba736f..d804d3734 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/TrustIdentityDataAccessObject.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/TrustIdentityDataAccessObject.java @@ -19,6 +19,8 @@ package org.sufficientlysecure.keychain.provider; +import java.util.Date; + import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; @@ -83,4 +85,33 @@ public class TrustIdentityDataAccessObject { return null; } + + public Date getLastUpdateForTrustId(String trustId) { + Cursor cursor = mQueryInterface.query(ApiTrustIdentity.buildByPackageNameAndTrustId(packageName, trustId), + null, null, null, null); + + try { + if (cursor != null && cursor.moveToFirst()) { + long lastUpdated = cursor.getColumnIndex(ApiTrustIdentity.LAST_UPDATED); + return new Date(lastUpdated); + } + } finally { + if (cursor != null) { + cursor.close(); + } + } + return null; + } + + public void setMasterKeyIdForTrustId(String trustId, long masterKeyId, Date date) { + Date lastUpdated = getLastUpdateForTrustId(trustId); + if (lastUpdated != null && lastUpdated.after(date)) { + throw new IllegalArgumentException("Database entry was newer than the one to be inserted! Cannot backdate"); + } + + ContentValues cv = new ContentValues(); + cv.put(ApiTrustIdentity.MASTER_KEY_ID, masterKeyId); + cv.put(ApiTrustIdentity.LAST_UPDATED, date.getTime()); + mQueryInterface.update(ApiTrustIdentity.buildByPackageNameAndTrustId(packageName, trustId), cv, null, null); + } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java index c514bc3f6..193964007 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java @@ -44,6 +44,7 @@ import org.bouncycastle.bcpg.ArmoredOutputStream; import org.openintents.openpgp.IOpenPgpService; import org.openintents.openpgp.OpenPgpDecryptionResult; import org.openintents.openpgp.OpenPgpError; +import org.openintents.openpgp.OpenPgpInlineKeyUpdate; import org.openintents.openpgp.OpenPgpMetadata; import org.openintents.openpgp.OpenPgpSignatureResult; import org.openintents.openpgp.OpenPgpSignatureResult.TrustIdentityResult; @@ -63,9 +64,11 @@ import org.sufficientlysecure.keychain.pgp.PgpSignEncryptData; import org.sufficientlysecure.keychain.pgp.PgpSignEncryptOperation; import org.sufficientlysecure.keychain.pgp.Progressable; import org.sufficientlysecure.keychain.pgp.SecurityProblem; +import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; import org.sufficientlysecure.keychain.provider.ApiDataAccessObject; import org.sufficientlysecure.keychain.provider.KeyRepository; +import org.sufficientlysecure.keychain.provider.KeyWritableRepository; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.OverriddenWarningsRepository; @@ -94,7 +97,6 @@ public class OpenPgpService extends Service { private ApiDataAccessObject mApiDao; private OpenPgpServiceKeyIdExtractor mKeyIdExtractor; private ApiPendingIntentFactory mApiPendingIntentFactory; - private TrustIdentityDataAccessObject mTrustIdentityDao; @Override public void onCreate() { @@ -102,8 +104,6 @@ public class OpenPgpService extends Service { mKeyRepository = KeyRepository.createDatabaseInteractor(this); mApiDao = new ApiDataAccessObject(this); mApiPermissionHelper = new ApiPermissionHelper(this, mApiDao); - mTrustIdentityDao = new TrustIdentityDataAccessObject(getBaseContext(), - mApiPermissionHelper.getCurrentCallingPackage()); mApiPendingIntentFactory = new ApiPendingIntentFactory(getBaseContext()); mKeyIdExtractor = OpenPgpServiceKeyIdExtractor.getInstance(getContentResolver(), mApiPendingIntentFactory); } @@ -366,6 +366,36 @@ public class OpenPgpService extends Service { byte[] detachedSignature = data.getByteArrayExtra(OpenPgpApi.EXTRA_DETACHED_SIGNATURE); String senderAddress = data.getStringExtra(OpenPgpApi.EXTRA_SENDER_ADDRESS); + String trustId = data.getStringExtra(OpenPgpApi.EXTRA_TRUST_IDENTITY); + OpenPgpInlineKeyUpdate inlineKeyUpdate = data.getParcelableExtra(OpenPgpApi.EXTRA_INLINE_KEY_DATA); + + UncachedKeyRing uncachedKeyRing = UncachedKeyRing.decodeFromData(inlineKeyUpdate.getKeyData()); + long inlineMasterKeyId = uncachedKeyRing.getMasterKeyId(); + // this will merge if the key already exists - no worries! + KeyWritableRepository.createDatabaseReadWriteInteractor(this).savePublicKeyRing(uncachedKeyRing); + + TrustIdentityDataAccessObject trustIdentityDao = new TrustIdentityDataAccessObject(getBaseContext(), + mApiPermissionHelper.getCurrentCallingPackage()); + + Date lastUpdate = trustIdentityDao.getLastUpdateForTrustId(trustId); + + Date updateTimestamp = inlineKeyUpdate.getTimestamp(); + boolean updateIsNewerThanLastUpdate = lastUpdate == null || lastUpdate.before(updateTimestamp); + if (updateIsNewerThanLastUpdate) { + Log.d(Constants.TAG, "Key for trust id is newer"); + + Long trustedMasterKeyId = trustIdentityDao.getMasterKeyIdForTrustId(trustId); + if (trustedMasterKeyId == null) { + Log.d(Constants.TAG, "No binding for trust id, pinning key"); + trustIdentityDao.setMasterKeyIdForTrustId(trustId, inlineMasterKeyId, updateTimestamp); + } else if (inlineMasterKeyId == trustedMasterKeyId) { + Log.d(Constants.TAG, "Key id is the same - doing nothing"); + } else { + // TODO danger in result intent! + trustIdentityDao.setMasterKeyIdForTrustId(trustId, inlineMasterKeyId, updateTimestamp); + } + } + PgpDecryptVerifyOperation op = new PgpDecryptVerifyOperation(this, mKeyRepository, progressable); long inputLength = data.getLongExtra(OpenPgpApi.EXTRA_DATA_LENGTH, InputData.UNKNOWN_FILESIZE); @@ -538,13 +568,13 @@ public class OpenPgpService extends Service { if (targetApiVersion < API_VERSION_WITH_TRUST_IDENTITIES) { throw new IllegalStateException("API version conflict, trust identities are supported v12 and up!"); } - signatureResult = addTrustIdentityInfoToSignatureResult(signatureResult, trustIdentity); + signatureResult = processTrustIdentityInfoToSignatureResult(signatureResult, trustIdentity); } result.putExtra(OpenPgpApi.RESULT_SIGNATURE, signatureResult); } - private OpenPgpSignatureResult addTrustIdentityInfoToSignatureResult(OpenPgpSignatureResult signatureResult, + private OpenPgpSignatureResult processTrustIdentityInfoToSignatureResult(OpenPgpSignatureResult signatureResult, String trustIdentity) { boolean hasValidSignature = signatureResult.getResult() == OpenPgpSignatureResult.RESULT_VALID_KEY_CONFIRMED || @@ -553,10 +583,13 @@ public class OpenPgpService extends Service { return signatureResult; } - Long tofuTrustedMasterKeyId = mTrustIdentityDao.getMasterKeyIdForTrustId(trustIdentity); + TrustIdentityDataAccessObject trustIdentityDao = new TrustIdentityDataAccessObject(getBaseContext(), + mApiPermissionHelper.getCurrentCallingPackage()); + Long tofuTrustedMasterKeyId = trustIdentityDao.getMasterKeyIdForTrustId(trustIdentity); long masterKeyId = signatureResult.getKeyId(); if (tofuTrustedMasterKeyId == null) { + trustIdentityDao.setMasterKeyIdForTrustId(trustIdentity, masterKeyId, new Date()); return signatureResult.withTrustIdentityResult(TrustIdentityResult.NEW); } else if (masterKeyId == tofuTrustedMasterKeyId) { return signatureResult.withTrustIdentityResult(TrustIdentityResult.OK); @@ -708,6 +741,57 @@ public class OpenPgpService extends Service { } } + private Intent updateTrustIdKeyImpl(Intent data) { + try { + Intent result = new Intent(); + + String trustId = data.getStringExtra(OpenPgpApi.EXTRA_TRUST_IDENTITY); + OpenPgpInlineKeyUpdate inlineKeyUpdate = data.getParcelableExtra(OpenPgpApi.EXTRA_INLINE_KEY_DATA); + if (inlineKeyUpdate == null || trustId == null) { + throw new IllegalArgumentException("need to specify both trust_id and inline_key_data!"); + } + + UncachedKeyRing uncachedKeyRing = UncachedKeyRing.decodeFromData(inlineKeyUpdate.getKeyData()); + long inlineMasterKeyId = uncachedKeyRing.getMasterKeyId(); + // this will merge if the key already exists - no worries! + KeyWritableRepository.createDatabaseReadWriteInteractor(this).savePublicKeyRing(uncachedKeyRing); + + TrustIdentityDataAccessObject trustIdentityDao = new TrustIdentityDataAccessObject(getBaseContext(), + mApiPermissionHelper.getCurrentCallingPackage()); + + Date lastUpdate = trustIdentityDao.getLastUpdateForTrustId(trustId); + + Date updateTimestamp = inlineKeyUpdate.getTimestamp(); + boolean updateIsNewerThanLastUpdate = lastUpdate == null || lastUpdate.before(updateTimestamp); + if (!updateIsNewerThanLastUpdate) { + result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS); + return result; + } + Log.d(Constants.TAG, "Key for trust id is newer"); + + Long trustedMasterKeyId = trustIdentityDao.getMasterKeyIdForTrustId(trustId); + if (trustedMasterKeyId == null) { + Log.d(Constants.TAG, "No binding for trust id, pinning key"); + trustIdentityDao.setMasterKeyIdForTrustId(trustId, inlineMasterKeyId, updateTimestamp); + } else if (inlineMasterKeyId == trustedMasterKeyId) { + Log.d(Constants.TAG, "Key id is the same - doing nothing"); + } else { + // TODO danger in result intent! + trustIdentityDao.setMasterKeyIdForTrustId(trustId, inlineMasterKeyId, updateTimestamp); + } + + result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS); + return result; + } catch (Exception e) { + Log.d(Constants.TAG, "exception in updateTrustIdKeyImpl", e); + Intent result = new Intent(); + result.putExtra(OpenPgpApi.RESULT_ERROR, + new OpenPgpError(OpenPgpError.GENERIC_ERROR, e.getMessage())); + result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR); + return result; + } + } + private Intent checkPermissionImpl(@NonNull Intent data) { Intent permissionIntent = mApiPermissionHelper.isAllowedOrReturnIntent(data); if (permissionIntent != null) { @@ -879,6 +963,9 @@ public class OpenPgpService extends Service { case OpenPgpApi.ACTION_BACKUP: { return backupImpl(data, outputStream); } + case OpenPgpApi.ACTION_UPDATE_TRUST_ID: { + return updateTrustIdKeyImpl(data); + } default: { return null; }