From f413d773609ed2e825a0dc1a5a2065b17407599b Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Fri, 22 Sep 2017 04:20:09 +0200 Subject: [PATCH] introduce updateTrustDb to use instead of a full consolidate --- .../keychain/operations/DeleteOperation.java | 11 ++- .../keychain/operations/ImportOperation.java | 23 +++--- .../operations/results/UpdateTrustResult.java | 50 ++++++++++++ .../provider/KeyWritableRepository.java | 79 +++++++++++++++++-- .../keychain/provider/KeychainDatabase.java | 2 +- 5 files changed, 144 insertions(+), 21 deletions(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/UpdateTrustResult.java diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/DeleteOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/DeleteOperation.java index 2932767b1..ce023f627 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/DeleteOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/DeleteOperation.java @@ -18,15 +18,17 @@ package org.sufficientlysecure.keychain.operations; +import java.util.Collections; + import android.content.Context; import android.support.annotation.NonNull; import org.sufficientlysecure.keychain.BuildConfig; -import org.sufficientlysecure.keychain.operations.results.ConsolidateResult; import org.sufficientlysecure.keychain.operations.results.DeleteResult; import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType; import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; +import org.sufficientlysecure.keychain.operations.results.UpdateTrustResult; import org.sufficientlysecure.keychain.pgp.Progressable; import org.sufficientlysecure.keychain.provider.KeyWritableRepository; import org.sufficientlysecure.keychain.service.ContactSyncAdapterService; @@ -92,10 +94,11 @@ public class DeleteOperation extends BaseReadWriteOperation } } - if (!BuildConfig.DEBUG && isSecret && success > 0) { + if (isSecret && success > 0) { log.add(LogType.MSG_DEL_CONSOLIDATE, 1); - ConsolidateResult sub = mKeyWritableRepository.consolidateDatabaseStep1(mProgressable); - log.add(sub, 2); + UpdateTrustResult sub = mKeyWritableRepository.updateTrustDb( + Collections.singletonList(masterKeyIds[0]), mProgressable); +// log.add(sub, 2); } int result = DeleteResult.RESULT_OK; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java index a6bc0233b..0dd60388f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java @@ -39,19 +39,19 @@ import android.support.annotation.Nullable; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.keyimport.FacebookKeyserverClient; +import org.sufficientlysecure.keychain.keyimport.HkpKeyserverAddress; import org.sufficientlysecure.keychain.keyimport.HkpKeyserverClient; import org.sufficientlysecure.keychain.keyimport.KeybaseKeyserverClient; import org.sufficientlysecure.keychain.keyimport.KeyserverClient; import org.sufficientlysecure.keychain.keyimport.KeyserverClient.QueryNotFoundException; -import org.sufficientlysecure.keychain.keyimport.HkpKeyserverAddress; import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing; import org.sufficientlysecure.keychain.network.orbot.OrbotHelper; -import org.sufficientlysecure.keychain.operations.results.ConsolidateResult; import org.sufficientlysecure.keychain.operations.results.ImportKeyResult; import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType; import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; import org.sufficientlysecure.keychain.operations.results.SaveKeyringResult; +import org.sufficientlysecure.keychain.operations.results.UpdateTrustResult; import org.sufficientlysecure.keychain.pgp.CanonicalizedKeyRing; import org.sufficientlysecure.keychain.pgp.Progressable; import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; @@ -155,7 +155,8 @@ public class ImportOperation extends BaseReadWriteOperation return new ImportKeyResult(ImportKeyResult.RESULT_FAIL_NOTHING, log); } - int newKeys = 0, updatedKeys = 0, missingKeys = 0, badKeys = 0, secret = 0; + int newKeys = 0, updatedKeys = 0, missingKeys = 0, badKeys = 0; + ArrayList secretMasterKeyIds = new ArrayList<>(); ArrayList importedMasterKeyIds = new ArrayList<>(); ArrayList canKeyRings = new ArrayList<>(); @@ -223,7 +224,8 @@ public class ImportOperation extends BaseReadWriteOperation if (key.isSecret()) { result = mKeyWritableRepository.saveSecretKeyRing(key, canKeyRings, skipSave); } else { - result = mKeyWritableRepository.savePublicKeyRing(key, entry.getExpectedFingerprint(), canKeyRings, skipSave); + result = mKeyWritableRepository.savePublicKeyRing(key, entry.getExpectedFingerprint(), canKeyRings, + false, skipSave); } } if (!result.success()) { @@ -235,7 +237,7 @@ public class ImportOperation extends BaseReadWriteOperation } else { newKeys += 1; if (key.isSecret()) { - secret += 1; + secretMasterKeyIds.add(key.getMasterKeyId()); } importedMasterKeyIds.add(key.getMasterKeyId()); } @@ -260,13 +262,13 @@ public class ImportOperation extends BaseReadWriteOperation // synchronized on mProviderHelper to prevent // https://github.com/open-keychain/open-keychain/issues/1221 since a consolidate deletes // and re-inserts keys, which could conflict with a parallel db key update - if (!skipSave && (secret > 0)) { + if (!skipSave && !secretMasterKeyIds.isEmpty()) { setPreventCancel(); - ConsolidateResult result; + UpdateTrustResult result; synchronized (mKeyRepository) { - result = mKeyWritableRepository.consolidateDatabaseStep1(progressable); + result = mKeyWritableRepository.updateTrustDb(secretMasterKeyIds, progressable); } - log.add(result, 1); + // log.add(result, 1); } // Special: make sure new data is synced into contacts @@ -320,7 +322,8 @@ public class ImportOperation extends BaseReadWriteOperation } ImportKeyResult result = new ImportKeyResult( - resultType, log, newKeys, updatedKeys, missingKeys, badKeys, secret, importedMasterKeyIdsArray); + resultType, log, newKeys, updatedKeys, missingKeys, badKeys, secretMasterKeyIds.size(), + importedMasterKeyIdsArray); result.setCanonicalizedKeyRings(canKeyRings); return result; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/UpdateTrustResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/UpdateTrustResult.java new file mode 100644 index 000000000..738b4f839 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/UpdateTrustResult.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2014 Dominik Schürmann + * Copyright (C) 2014 Vincent Breitmoser + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.sufficientlysecure.keychain.operations.results; + +import android.os.Parcel; + + +public class UpdateTrustResult extends OperationResult { + + public UpdateTrustResult(int result, OperationLog log) { + super(result, log); + } + + /** Construct from a parcel - trivial because we have no extra data. */ + public UpdateTrustResult(Parcel source) { + super(source); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + } + + public static Creator CREATOR = new Creator() { + public UpdateTrustResult createFromParcel(final Parcel source) { + return new UpdateTrustResult(source); + } + + public UpdateTrustResult[] newArray(final int size) { + return new UpdateTrustResult[size]; + } + }; + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeyWritableRepository.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeyWritableRepository.java index 33dd0f4f5..519a7bf23 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeyWritableRepository.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeyWritableRepository.java @@ -49,6 +49,7 @@ import org.sufficientlysecure.keychain.operations.results.ImportKeyResult; import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType; import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; import org.sufficientlysecure.keychain.operations.results.SaveKeyringResult; +import org.sufficientlysecure.keychain.operations.results.UpdateTrustResult; import org.sufficientlysecure.keychain.pgp.CanonicalizedKeyRing; import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKey; import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing; @@ -66,6 +67,7 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAutocryptPee import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeySignatures; import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; import org.sufficientlysecure.keychain.provider.KeychainContract.UpdatedKeys; import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; @@ -279,6 +281,8 @@ public class KeyWritableRepository extends KeyRepository { // otherwise the order in the keyfile is preserved. List uids = new ArrayList<>(); + List signerKeyIds = new ArrayList<>(); + if (trustedKeys.size() == 0) { log(LogType.MSG_IP_UID_CLASSIFYING_ZERO); } else { @@ -320,6 +324,13 @@ public class KeyWritableRepository extends KeyRepository { // do we have a trusted key for this? if (trustedKeys.indexOfKey(certId) < 0) { + if (!signerKeyIds.contains(certId)) { + operations.add(ContentProviderOperation.newInsert(KeySignatures.CONTENT_URI) + .withValue(KeySignatures.MASTER_KEY_ID, masterKeyId) + .withValue(KeySignatures.SIGNER_KEY_ID, certId) + .build()); + signerKeyIds.add(certId); + } unknownCerts += 1; continue; } @@ -744,9 +755,10 @@ public class KeyWritableRepository extends KeyRepository { * If you want to merge keys in-memory only and not save in database set skipSave=true. */ public SaveKeyringResult savePublicKeyRing(UncachedKeyRing publicRing, - byte[] expectedFingerprint, - ArrayList canKeyRings, - boolean skipSave) { + byte[] expectedFingerprint, + ArrayList canKeyRings, + boolean forceRefresh, + boolean skipSave) { try { long masterKeyId = publicRing.getMasterKeyId(); @@ -783,7 +795,7 @@ public class KeyWritableRepository extends KeyRepository { if (canKeyRings != null) canKeyRings.add(canPublicRing); // Early breakout if nothing changed - if (Arrays.hashCode(publicRing.getEncoded()) + if (!forceRefresh && Arrays.hashCode(publicRing.getEncoded()) == Arrays.hashCode(oldPublicRing.getEncoded())) { log(LogType.MSG_IP_SUCCESS_IDENTICAL); return new SaveKeyringResult(SaveKeyringResult.UPDATED, mLog, null); @@ -867,11 +879,20 @@ public class KeyWritableRepository extends KeyRepository { } public SaveKeyringResult savePublicKeyRing(UncachedKeyRing publicRing, byte[] expectedFingerprint) { - return savePublicKeyRing(publicRing, expectedFingerprint, null, false); + return savePublicKeyRing(publicRing, expectedFingerprint, null, false, false); + } + + public SaveKeyringResult savePublicKeyRing(UncachedKeyRing publicRing, byte[] expectedFingerprint, + boolean forceRefresh) { + return savePublicKeyRing(publicRing, expectedFingerprint, null, forceRefresh, false); } public SaveKeyringResult savePublicKeyRing(UncachedKeyRing keyRing) { - return savePublicKeyRing(keyRing, null); + return savePublicKeyRing(keyRing, null, false); + } + + public SaveKeyringResult savePublicKeyRing(UncachedKeyRing keyRing, boolean forceRefresh) { + return savePublicKeyRing(keyRing, null, forceRefresh); } public SaveKeyringResult saveSecretKeyRing(UncachedKeyRing secretRing, @@ -1003,6 +1024,52 @@ public class KeyWritableRepository extends KeyRepository { return saveSecretKeyRing(secretRing, null, false); } + @NonNull + public UpdateTrustResult updateTrustDb(List signerMasterKeyIds, Progressable progress) { + OperationLog log = new OperationLog(); + + Cursor cursor; + boolean needsSigningDbUpdate = false; // TODO remember if we ever refreshed all keys everything + if (needsSigningDbUpdate) { + cursor = mContentResolver.query(KeyRings.buildUnifiedKeyRingsUri(), + new String[] { KeyRings.MASTER_KEY_ID }, null, null, null); + } else { + String[] signerMasterKeyIdStrings = new String[signerMasterKeyIds.size()]; + int i = 0; + for (Long masterKeyId : signerMasterKeyIds) { + signerMasterKeyIdStrings[i++] = Long.toString(masterKeyId); + } + + cursor = mContentResolver.query(KeyRings.buildUnifiedKeyRingsFilterBySigner(), + new String[] { KeyRings.MASTER_KEY_ID }, null, signerMasterKeyIdStrings, null); + } + + if (cursor == null) { + throw new IllegalStateException(); + } + + try { + while (cursor.moveToNext()) { + try { + long masterKeyId = cursor.getLong(0); + + byte[] pubKeyData = loadPublicKeyRingData(masterKeyId); + UncachedKeyRing uncachedKeyRing = UncachedKeyRing.decodeFromData(pubKeyData); + SaveKeyringResult result = savePublicKeyRing(uncachedKeyRing, true); + + log.add(result, 1); + } catch (NotFoundException | PgpGeneralException | IOException e) { + Log.e(Constants.TAG, "Error updating trust database", e); + return new UpdateTrustResult(UpdateTrustResult.RESULT_ERROR, log); + } + } + + return new UpdateTrustResult(UpdateTrustResult.RESULT_OK, log); + } finally { + cursor.close(); + } + } + @NonNull public ConsolidateResult consolidateDatabaseStep1(Progressable progress) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java index 54ad080aa..d38fe3fde 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java @@ -37,8 +37,8 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAppsColumns; import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAutocryptPeerColumns; import org.sufficientlysecure.keychain.provider.KeychainContract.CertsColumns; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingsColumns; -import org.sufficientlysecure.keychain.provider.KeychainContract.KeysColumns; import org.sufficientlysecure.keychain.provider.KeychainContract.KeySignaturesColumns; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeysColumns; import org.sufficientlysecure.keychain.provider.KeychainContract.OverriddenWarnings; import org.sufficientlysecure.keychain.provider.KeychainContract.UpdatedKeysColumns; import org.sufficientlysecure.keychain.provider.KeychainContract.UserPacketsColumns;