From 31830a8c86421b58a6629d7c5b88c165ee0b8c74 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 26 Jun 2018 10:24:19 +0200 Subject: [PATCH] extract database access from CachedPublicKeyRing --- .../keychain/operations/BackupOperation.java | 69 ++--- .../keychain/operations/CertifyOperation.java | 4 +- .../keychain/operations/RevokeOperation.java | 16 +- .../pgp/OpenPgpSignatureResultBuilder.java | 2 +- .../pgp/PgpDecryptVerifyOperation.java | 5 +- .../keychain/pgp/PgpSignEncryptOperation.java | 9 +- .../keychain/provider/AbstractDao.java | 9 + .../provider/CachedPublicKeyRing.java | 235 +++--------------- .../keychain/provider/KeyRepository.java | 132 ++++------ .../provider/KeyWritableRepository.java | 46 +--- .../keychain/provider/KeychainContract.java | 13 - .../keychain/provider/KeychainDatabase.java | 15 +- .../keychain/provider/KeychainProvider.java | 39 +-- .../keychain/remote/OpenPgpService.java | 14 +- .../remote/SshAuthenticationService.java | 21 +- .../ui/RequestKeyPermissionPresenter.java | 2 +- .../service/PassphraseCacheService.java | 9 +- .../keychain/ssh/AuthenticationOperation.java | 16 +- .../keychain/ui/CertifyKeyFragment.java | 4 +- .../keychain/ui/CreateKeyFinalFragment.java | 6 +- .../keychain/ui/DeleteKeyDialogActivity.java | 89 +++---- .../ui/EncryptModeAsymmetricFragment.java | 3 +- .../keychain/ui/PassphraseDialogActivity.java | 10 +- .../ui/keyview/LinkedIdViewFragment.java | 7 +- .../keychain/ui/keyview/ViewKeyActivity.java | 3 +- .../ui/token/PublicKeyRetrievalLoader.java | 8 +- .../transfer/presenter/TransferPresenter.java | 2 +- .../org/sufficientlysecure/keychain/Certs.sq | 2 +- .../org/sufficientlysecure/keychain/Keys.sq | 27 +- .../keychain/KeychainTestRunner.java | 1 + .../AuthenticationOperationTest.java | 24 +- .../operations/BackupOperationTest.java | 21 +- .../operations/CertifyOperationTest.java | 20 +- .../operations/PromoteKeyOperationTest.java | 8 +- .../keychain/pgp/PgpEncryptDecryptTest.java | 3 +- .../keychain/pgp/PgpKeyOperationTest.java | 19 +- .../pgp/UncachedKeyringMergeTest.java | 13 +- .../keychain/provider/InteropTest.java | 108 ++------ .../provider/KeyRepositorySaveTest.java | 22 +- .../keychain/ssh/SshPublicKeyTest.java | 2 +- 40 files changed, 328 insertions(+), 730 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/BackupOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/BackupOperation.java index 7bb28029d..65bbd4d7c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/BackupOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/BackupOperation.java @@ -24,24 +24,21 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.text.SimpleDateFormat; -import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Locale; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.regex.Pattern; import android.content.Context; -import android.database.Cursor; import android.net.Uri; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.os.CancellationSignal; -import android.text.TextUtils; import org.bouncycastle.bcpg.ArmoredOutputStream; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.model.SubKey.UnifiedKeyInfo; import org.sufficientlysecure.keychain.operations.results.ExportResult; import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType; import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; @@ -56,14 +53,13 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.provider.KeyRepository; import org.sufficientlysecure.keychain.provider.KeyRepository.NotFoundException; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; -import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables; import org.sufficientlysecure.keychain.provider.TemporaryFileProvider; import org.sufficientlysecure.keychain.service.BackupKeyringParcel; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; -import org.sufficientlysecure.keychain.util.Numeric9x4PassphraseUtil; import org.sufficientlysecure.keychain.util.CountingOutputStream; import org.sufficientlysecure.keychain.util.InputData; +import org.sufficientlysecure.keychain.util.Numeric9x4PassphraseUtil; import org.sufficientlysecure.keychain.util.Passphrase; import timber.log.Timber; @@ -84,8 +80,6 @@ public class BackupOperation extends BaseOperation { KeyRings.MASTER_KEY_ID, KeyRings.HAS_ANY_SECRET }; - private static final int INDEX_MASTER_KEY_ID = 0; - private static final int INDEX_HAS_ANY_SECRET = 1; // this is a very simple matcher, we only need basic sanitization private static final Pattern HEADER_PATTERN = Pattern.compile("[a-zA-Z0-9_-]+: [^\\n]+"); @@ -224,42 +218,36 @@ public class BackupOperation extends BaseOperation { OutputStream outStream, List extraSecretKeyHeaders) { // noinspection unused TODO use these in a log entry int okSecret = 0, okPublic = 0; - int progress = 0; - Cursor cursor = queryForKeys(masterKeyIds); - - if (cursor == null || !cursor.moveToFirst()) { - log.add(LogType.MSG_BACKUP_ERROR_DB, 1); - return false; // new ExportResult(ExportResult.RESULT_ERROR, log); - } - try { - - int numKeys = cursor.getCount(); + List unifiedKeyInfos; + if (masterKeyIds == null) { + unifiedKeyInfos = mKeyRepository.getAllUnifiedKeyInfo(); + } else { + unifiedKeyInfos = mKeyRepository.getUnifiedKeyInfo(masterKeyIds); + } + int numKeys = unifiedKeyInfos.size(); updateProgress(mContext.getResources().getQuantityString(R.plurals.progress_exporting_key, numKeys), 0, numKeys); // For each public masterKey id - while (!cursor.isAfterLast()) { - - long masterKeyId = cursor.getLong(INDEX_MASTER_KEY_ID); - log.add(LogType.MSG_BACKUP_PUBLIC, 1, KeyFormattingUtils.beautifyKeyId(masterKeyId)); + for (UnifiedKeyInfo keyInfo : unifiedKeyInfos) { + log.add(LogType.MSG_BACKUP_PUBLIC, 1, KeyFormattingUtils.beautifyKeyId(keyInfo.master_key_id())); boolean publicKeyWriteOk = false; if (exportPublic) { - publicKeyWriteOk = writePublicKeyToStream(masterKeyId, log, outStream); + publicKeyWriteOk = writePublicKeyToStream(keyInfo.master_key_id(), log, outStream); if (publicKeyWriteOk) { okPublic += 1; } } if (publicKeyWriteOk || !exportPublic) { - boolean hasSecret = cursor.getInt(INDEX_HAS_ANY_SECRET) > 0; - if (exportSecret && hasSecret) { - log.add(LogType.MSG_BACKUP_SECRET, 2, KeyFormattingUtils.beautifyKeyId(masterKeyId)); - if (writeSecretKeyToStream(masterKeyId, log, outStream, extraSecretKeyHeaders)) { + if (exportSecret && keyInfo.has_any_secret()) { + log.add(LogType.MSG_BACKUP_SECRET, 2, KeyFormattingUtils.beautifyKeyId(keyInfo.master_key_id())); + if (writeSecretKeyToStream(keyInfo.master_key_id(), log, outStream, extraSecretKeyHeaders)) { okSecret += 1; } extraSecretKeyHeaders = null; @@ -267,7 +255,6 @@ public class BackupOperation extends BaseOperation { } updateProgress(progress++, numKeys); - cursor.moveToNext(); } updateProgress(R.string.progress_done, numKeys, numKeys); @@ -282,7 +269,6 @@ public class BackupOperation extends BaseOperation { } catch (Exception e) { Timber.e(e, "error closing stream"); } - cursor.close(); } return true; @@ -342,29 +328,4 @@ public class BackupOperation extends BaseOperation { } } - private Cursor queryForKeys(long[] masterKeyIds) { - String selection = null, selectionArgs[] = null; - - if (masterKeyIds != null) { - // convert long[] to String[] - selectionArgs = new String[masterKeyIds.length]; - for (int i = 0; i < masterKeyIds.length; i++) { - selectionArgs[i] = Long.toString(masterKeyIds[i]); - } - - // generates ?,?,? as placeholders for selectionArgs - String placeholders = TextUtils.join(",", - Collections.nCopies(masterKeyIds.length, "?")); - - // put together selection string - selection = Tables.KEYS + "." + KeyRings.MASTER_KEY_ID - + " IN (" + placeholders + ")"; - } - - return mKeyRepository.getContentResolver().query( - KeyRings.buildUnifiedKeyRingsUri(), PROJECTION, selection, selectionArgs, - Tables.KEYS + "." + KeyRings.MASTER_KEY_ID - ); - } - } \ No newline at end of file diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java index f6981d33a..ced8173cf 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java @@ -86,10 +86,8 @@ public class CertifyOperation extends BaseReadWriteOperation KeyFormattingUtils.beautifyKeyId(masterKeyId)); try { - - CachedPublicKeyRing keyRing = mKeyRepository.getCachedPublicKeyRing(masterKeyId); + UnifiedKeyInfo keyInfo = mKeyRepository.getUnifiedKeyInfo(masterKeyId); + if (keyInfo == null) { + log.add(OperationResult.LogType.MSG_REVOKE_ERROR_KEY_FAIL, 1); + return new RevokeResult(RevokeResult.RESULT_ERROR, log, masterKeyId); + } // check if this is a master secret key we can work with - switch (keyRing.getSecretKeyType(masterKeyId)) { + switch (mKeyRepository.getSecretKeyType(masterKeyId)) { case GNU_DUMMY: log.add(OperationResult.LogType.MSG_EK_ERROR_DUMMY, 1); return new RevokeResult(RevokeResult.RESULT_ERROR, log, masterKeyId); } SaveKeyringParcel.Builder saveKeyringParcel = - SaveKeyringParcel.buildChangeKeyringParcel(masterKeyId, keyRing.getFingerprint()); + SaveKeyringParcel.buildChangeKeyringParcel(masterKeyId, keyInfo.fingerprint()); // all revoke operations are made atomic as of now saveKeyringParcel.setUpdateOptions(revokeKeyringParcel.isShouldUpload(), true, @@ -93,7 +95,7 @@ public class RevokeOperation extends BaseReadWriteOperation return new RevokeResult(RevokeResult.RESULT_ERROR, log, masterKeyId); } - } catch (PgpKeyNotFoundException | KeyWritableRepository.NotFoundException e) { + } catch (KeyWritableRepository.NotFoundException e) { Timber.e(e, "could not find key to revoke"); log.add(OperationResult.LogType.MSG_REVOKE_ERROR_KEY_FAIL, 1); return new RevokeResult(RevokeResult.RESULT_ERROR, log, masterKeyId); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpSignatureResultBuilder.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpSignatureResultBuilder.java index 6ba5d4414..399b0152c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpSignatureResultBuilder.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpSignatureResultBuilder.java @@ -124,7 +124,7 @@ public class OpenPgpSignatureResultBuilder { } catch (PgpKeyNotFoundException e) { Timber.d("No primary user id in keyring with master key id " + signingRing.getMasterKeyId()); } - setSignatureKeyCertified(signingRing.getVerified() != VerificationStatus.UNVERIFIED); + setSignatureKeyCertified(signingRing.getVerified() == VerificationStatus.VERIFIED_SECRET); List allUserIds = signingRing.getUnorderedUserIds(); List confirmedUserIds = mKeyRepository.getConfirmedUserIds(signingRing.getMasterKeyId()); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java index 3fc54ef14..57220f9e7 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java @@ -71,7 +71,6 @@ import org.sufficientlysecure.keychain.pgp.SecurityProblem.EncryptionAlgorithmPr import org.sufficientlysecure.keychain.pgp.SecurityProblem.KeySecurityProblem; import org.sufficientlysecure.keychain.pgp.SecurityProblem.MissingMdc; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; -import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing; import org.sufficientlysecure.keychain.provider.KeyRepository; import org.sufficientlysecure.keychain.provider.KeyWritableRepository; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; @@ -633,7 +632,6 @@ public class PgpDecryptVerifyOperation extends BaseOperation T mapSingleRow(SupportSQLiteQuery query, Mapper mapper) { + try (Cursor cursor = getReadableDb().query(query)) { + if (cursor.moveToNext()) { + return mapper.map(cursor); + } + } + return null; + } + interface Mapper { T map(Cursor cursor); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java index ee20d5e6c..425dc587d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java @@ -18,16 +18,11 @@ package org.sufficientlysecure.keychain.provider; -import android.net.Uri; - -import org.sufficientlysecure.keychain.model.CustomColumnAdapters; +import org.sufficientlysecure.keychain.model.SubKey.UnifiedKeyInfo; import org.sufficientlysecure.keychain.pgp.CanonicalizedKeyRing.VerificationStatus; import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType; import org.sufficientlysecure.keychain.pgp.KeyRing; -import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; import org.sufficientlysecure.keychain.provider.KeyRepository.NotFoundException; -import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; -import timber.log.Timber; /** This implementation of KeyRing provides a cached view of PublicKeyRing @@ -47,234 +42,64 @@ import timber.log.Timber; * */ public class CachedPublicKeyRing extends KeyRing { + private UnifiedKeyInfo unifiedKeyInfo; - final KeyRepository mKeyRepository; - final Uri mUri; - - public CachedPublicKeyRing(KeyRepository keyRepository, Uri uri) { - mKeyRepository = keyRepository; - mUri = uri; + public CachedPublicKeyRing(UnifiedKeyInfo unifiedKeyInfo) { + this.unifiedKeyInfo = unifiedKeyInfo; } @Override - public long getMasterKeyId() throws PgpKeyNotFoundException { - try { - Object data = mKeyRepository.getGenericData(mUri, - KeychainContract.KeyRings.MASTER_KEY_ID, KeyRepository.FIELD_TYPE_INTEGER); - return (Long) data; - } catch (KeyWritableRepository.NotFoundException e) { - throw new PgpKeyNotFoundException(e); - } + public long getMasterKeyId() { + return unifiedKeyInfo.master_key_id(); } - /** - * Find the master key id related to a given query. The id will either be extracted from the - * query, which should work for all specific /key_rings/ queries, or will be queried if it can't. - */ - public long extractOrGetMasterKeyId() throws PgpKeyNotFoundException { - // try extracting from the uri first - String firstSegment = mUri.getPathSegments().get(1); - if (!"find".equals(firstSegment)) try { - return Long.parseLong(firstSegment); - } catch (NumberFormatException e) { - // didn't work? oh well. - Timber.d("Couldn't get masterKeyId from URI, querying..."); - } - return getMasterKeyId(); + public byte[] getFingerprint() { + return unifiedKeyInfo.fingerprint(); } - public byte[] getFingerprint() throws PgpKeyNotFoundException { - try { - Object data = mKeyRepository.getGenericData(mUri, - KeychainContract.KeyRings.FINGERPRINT, KeyRepository.FIELD_TYPE_BLOB); - return (byte[]) data; - } catch (KeyWritableRepository.NotFoundException e) { - throw new PgpKeyNotFoundException(e); - } - } - - public long getCreationTime() throws PgpKeyNotFoundException { - try { - Object data = mKeyRepository.getGenericData(mUri, - KeychainContract.KeyRings.CREATION, KeyRepository.FIELD_TYPE_INTEGER); - return (long) data; - } catch (KeyWritableRepository.NotFoundException e) { - throw new PgpKeyNotFoundException(e); - } + public long getCreationTime() { + return unifiedKeyInfo.creation(); } @Override - public String getPrimaryUserId() throws PgpKeyNotFoundException { - try { - Object data = mKeyRepository.getGenericData(mUri, - KeychainContract.KeyRings.USER_ID, - KeyRepository.FIELD_TYPE_STRING); - return (String) data; - } catch(KeyWritableRepository.NotFoundException e) { - throw new PgpKeyNotFoundException(e); - } + public String getPrimaryUserId() { + return unifiedKeyInfo.user_id(); } - public String getPrimaryUserIdWithFallback() throws PgpKeyNotFoundException { + public String getPrimaryUserIdWithFallback() { return getPrimaryUserId(); } - public String getName() throws PgpKeyNotFoundException { - try { - Object data = mKeyRepository.getGenericData(mUri, - KeyRings.NAME, - KeyRepository.FIELD_TYPE_STRING); - return (String) data; - } catch(KeyWritableRepository.NotFoundException e) { - throw new PgpKeyNotFoundException(e); - } - } - - public String getEmail() throws PgpKeyNotFoundException { - try { - Object data = mKeyRepository.getGenericData(mUri, - KeyRings.EMAIL, - KeyRepository.FIELD_TYPE_STRING); - return (String) data; - } catch(KeyWritableRepository.NotFoundException e) { - throw new PgpKeyNotFoundException(e); - } - } - - - public String getComment() throws PgpKeyNotFoundException { - try { - Object data = mKeyRepository.getGenericData(mUri, - KeyRings.COMMENT, - KeyRepository.FIELD_TYPE_STRING); - return (String) data; - } catch(KeyWritableRepository.NotFoundException e) { - throw new PgpKeyNotFoundException(e); - } + @Override + public boolean isRevoked() { + return unifiedKeyInfo.is_revoked(); } @Override - public boolean isRevoked() throws PgpKeyNotFoundException { - try { - Object data = mKeyRepository.getGenericData(mUri, - KeychainContract.KeyRings.IS_REVOKED, - KeyRepository.FIELD_TYPE_INTEGER); - return (Long) data > 0; - } catch(KeyWritableRepository.NotFoundException e) { - throw new PgpKeyNotFoundException(e); - } + public boolean canCertify() { + return unifiedKeyInfo.can_certify(); } @Override - public boolean canCertify() throws PgpKeyNotFoundException { - try { - Object data = mKeyRepository.getGenericData(mUri, - KeychainContract.KeyRings.HAS_CERTIFY_SECRET, - KeyRepository.FIELD_TYPE_NULL); - return !((Boolean) data); - } catch(KeyWritableRepository.NotFoundException e) { - throw new PgpKeyNotFoundException(e); - } + public long getEncryptId() { + return unifiedKeyInfo.has_encrypt_key_int(); } @Override - public long getEncryptId() throws PgpKeyNotFoundException { - try { - Object data = mKeyRepository.getGenericData(mUri, - KeyRings.HAS_ENCRYPT, - KeyRepository.FIELD_TYPE_INTEGER); - return (Long) data; - } catch(KeyWritableRepository.NotFoundException e) { - throw new PgpKeyNotFoundException(e); - } + public boolean hasEncrypt() { + return unifiedKeyInfo.has_encrypt_key(); + } + + public long getAuthenticationId() { + return unifiedKeyInfo.has_auth_key_int(); } @Override - public boolean hasEncrypt() throws PgpKeyNotFoundException { - return getEncryptId() != 0; + public VerificationStatus getVerified() { + return unifiedKeyInfo.verified(); } - /** Returns the key id which should be used for signing. - * - * This method returns keys which are actually available (ie. secret available, and not stripped, - * revoked, or expired), hence only works on keyrings where a secret key is available! - * - */ - public long getSecretSignId() throws PgpKeyNotFoundException { - try { - Object data = mKeyRepository.getGenericData(mUri, - KeyRings.HAS_SIGN_SECRET, - KeyRepository.FIELD_TYPE_INTEGER); - return (Long) data; - } catch(KeyWritableRepository.NotFoundException e) { - throw new PgpKeyNotFoundException(e); - } - } - - /** Returns the key id which should be used for authentication. - * - * This method returns keys which are actually available (ie. secret available, and not stripped, - * revoked, or expired), hence only works on keyrings where a secret key is available! - * - */ - public long getSecretAuthenticationId() throws PgpKeyNotFoundException { - try { - Object data = mKeyRepository.getGenericData(mUri, - KeyRings.HAS_AUTHENTICATE_SECRET, - KeyRepository.FIELD_TYPE_INTEGER); - return (Long) data; - } catch(KeyWritableRepository.NotFoundException e) { - throw new PgpKeyNotFoundException(e); - } - } - - public long getAuthenticationId() throws PgpKeyNotFoundException { - try { - Object data = mKeyRepository.getGenericData(mUri, - KeyRings.HAS_AUTHENTICATE, - KeyRepository.FIELD_TYPE_INTEGER); - return (Long) data; - } catch(KeyWritableRepository.NotFoundException e) { - throw new PgpKeyNotFoundException(e); - } - } - - @Override - public VerificationStatus getVerified() throws PgpKeyNotFoundException { - try { - Object data = mKeyRepository.getGenericData(mUri, - KeychainContract.KeyRings.VERIFIED, - KeyRepository.FIELD_TYPE_INTEGER); - return CustomColumnAdapters.VERIFICATON_STATUS_ADAPTER.decode((Long) data); - } catch(KeyWritableRepository.NotFoundException e) { - throw new PgpKeyNotFoundException(e); - } - } - - public boolean hasAnySecret() throws PgpKeyNotFoundException { - try { - Object data = mKeyRepository.getGenericData(mUri, - KeychainContract.KeyRings.HAS_ANY_SECRET, - KeyRepository.FIELD_TYPE_INTEGER); - return (Long) data > 0; - } catch(KeyWritableRepository.NotFoundException e) { - throw new PgpKeyNotFoundException(e); - } - } - - public SecretKeyType getSecretKeyType(long keyId) throws NotFoundException { - SecretKeyType secretKeyType = mKeyRepository.getSecretKeyType(keyId); - if (secretKeyType == null) { - throw new NotFoundException(); - } - return secretKeyType; - } - - public byte[] getEncoded() throws PgpKeyNotFoundException { - try { - return mKeyRepository.loadPublicKeyRingData(getMasterKeyId()); - } catch(KeyWritableRepository.NotFoundException e) { - throw new PgpKeyNotFoundException(e); - } + public boolean hasAnySecret() { + return unifiedKeyInfo.has_any_secret(); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeyRepository.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeyRepository.java index e6d997c92..4c60c0c67 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeyRepository.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeyRepository.java @@ -21,13 +21,11 @@ package org.sufficientlysecure.keychain.provider; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import android.content.ContentResolver; import android.content.Context; import android.database.Cursor; -import android.net.Uri; import com.squareup.sqldelight.SqlDelightQuery; import org.bouncycastle.bcpg.ArmoredOutputStream; @@ -43,21 +41,10 @@ import org.sufficientlysecure.keychain.pgp.CanonicalizedKeyRing.VerificationStat import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing; import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType; import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing; -import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; -import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; -import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import timber.log.Timber; public class KeyRepository extends AbstractDao { - // If we ever switch to api level 11, we can ditch this whole mess! - public static final int FIELD_TYPE_NULL = 1; - // this is called integer to stay coherent with the constants in Cursor (api level 11) - public static final int FIELD_TYPE_INTEGER = 2; - public static final int FIELD_TYPE_FLOAT = 3; - public static final int FIELD_TYPE_STRING = 4; - public static final int FIELD_TYPE_BLOB = 5; - final ContentResolver contentResolver; final LocalPublicKeyStorage mLocalPublicKeyStorage; final LocalSecretKeyStorage localSecretKeyStorage; @@ -115,72 +102,13 @@ public class KeyRepository extends AbstractDao { mLog = new OperationLog(); } - Object getGenericData(Uri uri, String column, int type) throws NotFoundException { - Object result = getGenericData(uri, new String[]{column}, new int[]{type}, null).get(column); - if (result == null) { + // replace with getUnifiedKeyInfo + public CachedPublicKeyRing getCachedPublicKeyRing(long masterKeyId) throws NotFoundException { + UnifiedKeyInfo unifiedKeyInfo = getUnifiedKeyInfo(masterKeyId); + if (unifiedKeyInfo == null) { throw new NotFoundException(); } - return result; - } - - private HashMap getGenericData(Uri uri, String[] proj, int[] types) - throws NotFoundException { - return getGenericData(uri, proj, types, null); - } - - private HashMap getGenericData(Uri uri, String[] proj, int[] types, String selection) - throws NotFoundException { - Cursor cursor = contentResolver.query(uri, proj, selection, null, null); - - try { - HashMap result = new HashMap<>(proj.length); - if (cursor != null && cursor.moveToFirst()) { - int pos = 0; - for (String p : proj) { - switch (types[pos]) { - case FIELD_TYPE_NULL: - result.put(p, cursor.isNull(pos)); - break; - case FIELD_TYPE_INTEGER: - result.put(p, cursor.getLong(pos)); - break; - case FIELD_TYPE_FLOAT: - result.put(p, cursor.getFloat(pos)); - break; - case FIELD_TYPE_STRING: - result.put(p, cursor.getString(pos)); - break; - case FIELD_TYPE_BLOB: - result.put(p, cursor.getBlob(pos)); - break; - } - pos += 1; - } - } else { - // If no data was found, throw an appropriate exception - throw new NotFoundException(); - } - - return result; - } finally { - if (cursor != null) { - cursor.close(); - } - } - } - - public HashMap getUnifiedData(long masterKeyId, String[] proj, int[] types) - throws NotFoundException { - return getGenericData(KeyRings.buildUnifiedKeyRingUri(masterKeyId), proj, types); - } - - public CachedPublicKeyRing getCachedPublicKeyRing(Uri queryUri) throws PgpKeyNotFoundException { - long masterKeyId = new CachedPublicKeyRing(this, queryUri).extractOrGetMasterKeyId(); - return getCachedPublicKeyRing(masterKeyId); - } - - public CachedPublicKeyRing getCachedPublicKeyRing(long id) { - return new CachedPublicKeyRing(this, KeyRings.buildUnifiedKeyRingUri(id)); + return new CachedPublicKeyRing(unifiedKeyInfo); } public CanonicalizedPublicKeyRing getCanonicalizedPublicKeyRing(long masterKeyId) throws NotFoundException { @@ -211,11 +139,7 @@ public class KeyRepository extends AbstractDao { } public List getMasterKeyIdsBySigner(List signerMasterKeyIds) { - long[] signerKeyIds = new long[signerMasterKeyIds.size()]; - int i = 0; - for (Long signerKeyId : signerMasterKeyIds) { - signerKeyIds[i++] = signerKeyId; - } + long[] signerKeyIds = getLongListAsArray(signerMasterKeyIds); SqlDelightQuery query = SubKey.FACTORY.selectMasterKeyIdsBySigner(signerKeyIds); return mapAllRows(query, KeyRingPublic.FACTORY.selectAllMasterKeyIdsMapper()::map); } @@ -240,6 +164,11 @@ public class KeyRepository extends AbstractDao { } } + public List getUnifiedKeyInfo(long... masterKeyIds) { + SqlDelightQuery query = SubKey.FACTORY.selectUnifiedKeyInfoByMasterKeyIds(masterKeyIds); + return mapAllRows(query, SubKey.UNIFIED_KEY_INFO_MAPPER::map); + } + public List getUnifiedKeyInfosByMailAddress(String mailAddress) { SqlDelightQuery query = SubKey.FACTORY.selectUnifiedKeyInfoSearchMailAddress('%' + mailAddress + '%'); return mapAllRows(query, SubKey.UNIFIED_KEY_INFO_MAPPER::map); @@ -275,17 +204,17 @@ public class KeyRepository extends AbstractDao { return mapAllRows(query, SubKey.SUBKEY_MAPPER::map); } - public SecretKeyType getSecretKeyType(long keyId) { + public SecretKeyType getSecretKeyType(long keyId) throws NotFoundException { SqlDelightQuery query = SubKey.FACTORY.selectSecretKeyType(keyId); try (Cursor cursor = getReadableDb().query(query)) { if (cursor.moveToFirst()) { return SubKey.SKT_MAPPER.map(cursor); } - return null; + throw new NotFoundException(); } } - private byte[] getKeyRingAsArmoredData(byte[] data) throws IOException, PgpGeneralException { + private byte[] getKeyRingAsArmoredData(byte[] data) throws IOException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ArmoredOutputStream aos = new ArmoredOutputStream(bos); @@ -295,15 +224,13 @@ public class KeyRepository extends AbstractDao { return bos.toByteArray(); } - public String getPublicKeyRingAsArmoredString(long masterKeyId) - throws NotFoundException, IOException, PgpGeneralException { + public String getPublicKeyRingAsArmoredString(long masterKeyId) throws NotFoundException, IOException { byte[] data = loadPublicKeyRingData(masterKeyId); byte[] armoredData = getKeyRingAsArmoredData(data); return new String(armoredData); } - public byte[] getSecretKeyRingAsArmoredData(long masterKeyId) - throws NotFoundException, IOException, PgpGeneralException { + public byte[] getSecretKeyRingAsArmoredData(long masterKeyId) throws NotFoundException, IOException { byte[] data = loadSecretKeyRingData(masterKeyId); return getKeyRingAsArmoredData(data); } @@ -338,6 +265,24 @@ public class KeyRepository extends AbstractDao { } } + public long getSecretSignId(long masterKeyId) throws NotFoundException { + SqlDelightQuery query = SubKey.FACTORY.selectEffectiveSignKeyIdByMasterKeyId(masterKeyId); + Long signKeyId = mapSingleRow(query, SubKey.FACTORY.selectEffectiveSignKeyIdByMasterKeyIdMapper()::map); + if (signKeyId == null) { + throw new NotFoundException(); + } + return signKeyId; + } + + public Long getSecretAuthenticationId(long masterKeyId) throws NotFoundException { + SqlDelightQuery query = SubKey.FACTORY.selectEffectiveAuthKeyIdByMasterKeyId(masterKeyId); + Long authKeyId = mapSingleRow(query, SubKey.FACTORY.selectEffectiveAuthKeyIdByMasterKeyIdMapper()::map); + if (authKeyId == null) { + throw new NotFoundException(); + } + return authKeyId; + } + public static class NotFoundException extends Exception { public NotFoundException() { } @@ -346,4 +291,13 @@ public class KeyRepository extends AbstractDao { super(name); } } + + private long[] getLongListAsArray(List longList) { + long[] longs = new long[longList.size()]; + int i = 0; + for (Long aLong : longList) { + longs[i++] = aLong; + } + return longs; + } } 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 8d42d78cf..0a6786d76 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeyWritableRepository.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeyWritableRepository.java @@ -29,7 +29,6 @@ import android.content.ContentProviderOperation; import android.content.ContentValues; import android.content.Context; import android.content.OperationApplicationException; -import android.database.Cursor; import android.net.Uri; import android.os.RemoteException; import android.support.annotation.NonNull; @@ -40,6 +39,7 @@ import org.openintents.openpgp.util.OpenPgpUtils; import org.sufficientlysecure.keychain.KeyRingsPublicModel.DeleteByMasterKeyId; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.model.CustomColumnAdapters; +import org.sufficientlysecure.keychain.model.SubKey.UnifiedKeyInfo; import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType; import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; import org.sufficientlysecure.keychain.operations.results.SaveKeyringResult; @@ -60,7 +60,6 @@ import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; 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.UserPackets; @@ -99,8 +98,7 @@ public class KeyWritableRepository extends KeyRepository { localPublicKeyStorage, localSecretKeyStorage, databaseNotifyManager, autocryptPeerDao); } - @VisibleForTesting - KeyWritableRepository(Context context, + private KeyWritableRepository(Context context, KeychainDatabase database, LocalPublicKeyStorage localPublicKeyStorage, LocalSecretKeyStorage localSecretKeyStorage, DatabaseNotifyManager databaseNotifyManager, AutocryptPeerDao autocryptPeerDao) { @@ -120,40 +118,22 @@ public class KeyWritableRepository extends KeyRepository { } private LongSparseArray getTrustedMasterKeys() { - Cursor cursor = contentResolver.query(KeyRings.buildUnifiedKeyRingsUri(), new String[] { - KeyRings.MASTER_KEY_ID, - // we pick from cache only information that is not easily available from keyrings - KeyRings.HAS_ANY_SECRET, KeyRings.VERIFIED - }, KeyRings.HAS_ANY_SECRET + " = 1", null, null); + LongSparseArray result = new LongSparseArray<>(); - try { - LongSparseArray result = new LongSparseArray<>(); - - if (cursor == null) { - return result; - } - - while (cursor.moveToNext()) { - try { - long masterKeyId = cursor.getLong(0); - long verified = cursor.getLong(2); - byte[] blob = loadPublicKeyRingData(masterKeyId); - VerificationStatus verificationStatus = CustomColumnAdapters.VERIFICATON_STATUS_ADAPTER.decode(verified); - if (blob != null) { - result.put(masterKeyId, new CanonicalizedPublicKeyRing(blob, verificationStatus).getPublicKey()); - } - } catch (NotFoundException e) { - throw new IllegalStateException("Error reading secret key data, this should not happen!", e); + List unifiedKeyInfoWithSecret = getAllUnifiedKeyInfoWithSecret(); + for (UnifiedKeyInfo unifiedKeyInfo : unifiedKeyInfoWithSecret) { + try { + byte[] blob = loadPublicKeyRingData(unifiedKeyInfo.master_key_id()); + if (blob != null) { + result.put(unifiedKeyInfo.master_key_id(), + new CanonicalizedPublicKeyRing(blob, unifiedKeyInfo.verified()).getPublicKey()); } - } - - return result; - } finally { - if (cursor != null) { - cursor.close(); + } catch (NotFoundException e) { + throw new IllegalStateException("Error reading secret key data, this should not happen!", e); } } + return result; } // bits, in order: CESA. make SURE these are correct, we will get bad log entries otherwise!! diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java index edb8ea0a9..bba2fcd58 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java @@ -99,9 +99,6 @@ public class KeychainContract { public static final String PATH_UNIFIED = "unified"; - public static final String PATH_FIND = "find"; - public static final String PATH_BY_SUBKEY = "subkey"; - public static final String PATH_PUBLIC = "public"; public static final String PATH_USER_IDS = "user_ids"; public static final String PATH_KEYS = "keys"; @@ -132,16 +129,6 @@ public class KeychainContract { public static Uri buildGenericKeyRingUri(long masterKeyId) { return CONTENT_URI.buildUpon().appendPath(Long.toString(masterKeyId)).build(); } - - public static Uri buildUnifiedKeyRingUri(long masterKeyId) { - return CONTENT_URI.buildUpon().appendPath(Long.toString(masterKeyId)) - .appendPath(PATH_UNIFIED).build(); - } - - public static Uri buildUnifiedKeyRingsFindBySubkeyUri(long subkey) { - return CONTENT_URI.buildUpon().appendPath(PATH_FIND) - .appendPath(PATH_BY_SUBKEY).appendPath(Long.toString(subkey)).build(); - } } public static class KeyRingData implements KeyRingsColumns, BaseColumns { 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 da0cbb543..ac865bfc4 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java @@ -32,7 +32,9 @@ import android.content.Context; import android.database.Cursor; import android.database.SQLException; import android.database.sqlite.SQLiteException; +import android.support.annotation.VisibleForTesting; +import org.sufficientlysecure.keychain.ApiAllowedKeysModel; import org.sufficientlysecure.keychain.ApiAppsModel; import org.sufficientlysecure.keychain.AutocryptPeersModel; import org.sufficientlysecure.keychain.CertsModel; @@ -66,12 +68,15 @@ public class KeychainDatabase { private static KeychainDatabase sInstance; public static KeychainDatabase getInstance(Context context) { - if (sInstance == null) { sInstance = new KeychainDatabase(context.getApplicationContext()); - } return sInstance; } + @VisibleForTesting + public static void resetSingleton() { + sInstance = null; + } + public interface Tables { String KEY_RINGS_PUBLIC = "keyrings_public"; String KEYS = "keys"; @@ -133,7 +138,7 @@ public class KeychainDatabase { db.execSQL(ApiAppsModel.CREATE_TABLE); db.execSQL(OverriddenWarningsModel.CREATE_TABLE); db.execSQL(AutocryptPeersModel.CREATE_TABLE); - db.execSQL(ApiAppsModel.CREATE_TABLE); + db.execSQL(ApiAllowedKeysModel.CREATE_TABLE); db.execSQL(KeysModel.UNIFIEDKEYVIEW); db.execSQL("CREATE INDEX keys_by_rank ON keys (" + KeysColumns.RANK + ", " + KeysColumns.MASTER_KEY_ID + ");"); @@ -460,8 +465,8 @@ public class KeychainDatabase { // DANGEROUS, use in test code ONLY! public void clearDatabase() { - getWritableDatabase().execSQL("delete from " + Tables.KEY_RINGS_PUBLIC); - getWritableDatabase().execSQL("delete from " + Tables.API_ALLOWED_KEYS); + getWritableDatabase().execSQL("delete from " + KeyRingsPublicModel.TABLE_NAME); + getWritableDatabase().execSQL("delete from " + ApiAllowedKeysModel.TABLE_NAME); getWritableDatabase().execSQL("delete from api_apps"); } 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 800a3e5ad..89b0e5cbb 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -61,8 +61,6 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe private static final int KEY_RING_PUBLIC = 203; private static final int KEY_RING_CERTS = 205; - private static final int KEY_RINGS_FIND_BY_SUBKEY = 401; - private static final int KEY_SIGNATURES = 700; protected UriMatcher mUriMatcher; @@ -88,16 +86,6 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe + "/" + KeychainContract.PATH_UNIFIED, KEY_RINGS_UNIFIED); - /* - * find by criteria other than master key id - * - * key_rings/find/subkey/_ - * - */ - matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/" - + KeychainContract.PATH_FIND + "/" + KeychainContract.PATH_BY_SUBKEY + "/*", - KEY_RINGS_FIND_BY_SUBKEY); - /* * list key_ring specifics * @@ -174,8 +162,7 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe switch (match) { case KEY_RING_UNIFIED: - case KEY_RINGS_UNIFIED: - case KEY_RINGS_FIND_BY_SUBKEY: { + case KEY_RINGS_UNIFIED: { HashMap projectionMap = new HashMap<>(); projectionMap.put(KeyRings._ID, Tables.KEYS + ".oid AS _id"); projectionMap.put(KeyRings.MASTER_KEY_ID, Tables.KEYS + "." + Keys.MASTER_KEY_ID); @@ -318,28 +305,8 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe // in case there are multiple verifying certificates groupBy = Tables.KEYS + "." + Keys.MASTER_KEY_ID; - switch(match) { - case KEY_RING_UNIFIED: { - qb.appendWhere(" AND " + Tables.KEYS + "." + Keys.MASTER_KEY_ID + " = "); - qb.appendWhereEscapeString(uri.getPathSegments().get(1)); - break; - } - case KEY_RINGS_FIND_BY_SUBKEY: { - try { - String subkey = Long.valueOf(uri.getLastPathSegment()).toString(); - qb.appendWhere(" AND EXISTS (" - + " SELECT 1 FROM " + Tables.KEYS + " AS tmp" - + " WHERE tmp." + UserPackets.MASTER_KEY_ID - + " = " + Tables.KEYS + "." + Keys.MASTER_KEY_ID - + " AND tmp." + Keys.KEY_ID + " = " + subkey + "" - + ")"); - } catch(NumberFormatException e) { - Timber.e(e, "Malformed find by subkey query!"); - qb.appendWhere(" AND 0"); - } - break; - } - } + qb.appendWhere(" AND " + Tables.KEYS + "." + Keys.MASTER_KEY_ID + " = "); + qb.appendWhereEscapeString(uri.getPathSegments().get(1)); if (TextUtils.isEmpty(sortOrder)) { sortOrder = Tables.USER_PACKETS + "." + UserPackets.USER_ID + " ASC"; 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 2f29fc281..c6ee125de 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java @@ -66,12 +66,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.exception.PgpKeyNotFoundException; import org.sufficientlysecure.keychain.provider.ApiAppDao; import org.sufficientlysecure.keychain.provider.AutocryptPeerDao; import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing; import org.sufficientlysecure.keychain.provider.KeyRepository; -import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; +import org.sufficientlysecure.keychain.provider.KeyRepository.NotFoundException; import org.sufficientlysecure.keychain.provider.KeychainExternalContract.AutocryptStatus; import org.sufficientlysecure.keychain.provider.OverriddenWarningsRepository; import org.sufficientlysecure.keychain.remote.OpenPgpServiceKeyIdExtractor.KeyIdResult; @@ -139,9 +138,9 @@ public class OpenPgpService extends Service { // get first usable subkey capable of signing try { - long signSubKeyId = mKeyRepository.getCachedPublicKeyRing(signKeyId).getSecretSignId(); + long signSubKeyId = mKeyRepository.getSecretSignId(signKeyId); pgpData.setSignatureSubKeyId(signSubKeyId); - } catch (PgpKeyNotFoundException e) { + } catch (NotFoundException e) { throw new Exception("signing subkey not found!", e); } } @@ -230,7 +229,7 @@ public class OpenPgpService extends Service { if (signKeyId == Constants.key.none) { throw new Exception("No signing key given"); } - long signSubKeyId = mKeyRepository.getCachedPublicKeyRing(signKeyId).getSecretSignId(); + long signSubKeyId = mKeyRepository.getSecretSignId(signKeyId); pgpData.setSignatureMasterKeyId(signKeyId) .setSignatureSubKeyId(signSubKeyId) @@ -627,7 +626,8 @@ public class OpenPgpService extends Service { } try { - CanonicalizedPublicKeyRing keyRing = mKeyRepository.getCanonicalizedPublicKeyRing(masterKeyId); + CanonicalizedPublicKeyRing keyRing = + mKeyRepository.getCanonicalizedPublicKeyRing(masterKeyId); Intent result = new Intent(); result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS); @@ -749,7 +749,7 @@ public class OpenPgpService extends Service { result.putExtra(OpenPgpApi.RESULT_PRIMARY_USER_ID, userId); result.putExtra(OpenPgpApi.RESULT_KEY_CREATION_TIME, creationTime); - } catch (PgpKeyNotFoundException e) { + } catch (NotFoundException e) { Timber.e(e, "Error loading key info"); return createErrorResultIntent(OpenPgpError.GENERIC_ERROR, e.getMessage()); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/SshAuthenticationService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/SshAuthenticationService.java index 4851d0815..536bab373 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/SshAuthenticationService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/SshAuthenticationService.java @@ -48,6 +48,7 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; import org.sufficientlysecure.keychain.provider.ApiAppDao; import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing; import org.sufficientlysecure.keychain.provider.KeyRepository; +import org.sufficientlysecure.keychain.provider.KeyRepository.NotFoundException; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.service.input.RequiredInputParcel; import org.sufficientlysecure.keychain.ssh.AuthenticationData; @@ -59,8 +60,6 @@ import timber.log.Timber; public class SshAuthenticationService extends Service { - private static final String TAG = "SshAuthService"; - private ApiPermissionHelper mApiPermissionHelper; private KeyRepository mKeyRepository; private ApiAppDao mApiAppDao; @@ -144,23 +143,18 @@ public class SshAuthenticationService extends Service { AuthenticationData.Builder authData = AuthenticationData.builder(); authData.setAuthenticationMasterKeyId(masterKeyId); - CachedPublicKeyRing cachedPublicKeyRing = mKeyRepository.getCachedPublicKeyRing(masterKeyId); - long authSubKeyId; int authSubKeyAlgorithm; String authSubKeyCurveOid = null; try { // get first usable subkey capable of authentication - authSubKeyId = cachedPublicKeyRing.getSecretAuthenticationId(); + authSubKeyId = mKeyRepository.getSecretAuthenticationId(masterKeyId); // needed for encoding the resulting signature authSubKeyAlgorithm = getPublicKey(masterKeyId).getAlgorithm(); if (authSubKeyAlgorithm == PublicKeyAlgorithmTags.ECDSA) { authSubKeyCurveOid = getPublicKey(masterKeyId).getCurveOid(); } - } catch (PgpKeyNotFoundException e) { - return createExceptionErrorResult(SshAuthenticationApiError.NO_AUTH_KEY, - "authentication key for master key id not found in keychain", e); - } catch (KeyRepository.NotFoundException e) { + } catch (NotFoundException e) { return createExceptionErrorResult(SshAuthenticationApiError.NO_SUCH_KEY, "Key for master key id not found", e); } @@ -272,7 +266,7 @@ public class SshAuthenticationService extends Service { try { description = getDescription(masterKeyId); - } catch (PgpKeyNotFoundException e) { + } catch (NotFoundException e) { return createExceptionErrorResult(SshAuthenticationApiError.NO_SUCH_KEY, "Could not create description", e); } @@ -372,8 +366,7 @@ public class SshAuthenticationService extends Service { return new SshPublicKeyResponse(sshPublicKeyBlob).toIntent(); } - private CanonicalizedPublicKey getPublicKey(long masterKeyId) - throws PgpKeyNotFoundException, KeyRepository.NotFoundException { + private CanonicalizedPublicKey getPublicKey(long masterKeyId) throws NotFoundException { KeyRepository keyRepository = KeyRepository.create(getApplicationContext()); long authSubKeyId = keyRepository.getCachedPublicKeyRing(masterKeyId) .getAuthenticationId(); @@ -381,11 +374,11 @@ public class SshAuthenticationService extends Service { .getPublicKey(authSubKeyId); } - private String getDescription(long masterKeyId) throws PgpKeyNotFoundException { + private String getDescription(long masterKeyId) throws NotFoundException { CachedPublicKeyRing cachedPublicKeyRing = mKeyRepository.getCachedPublicKeyRing(masterKeyId); String description = ""; - long authSubKeyId = cachedPublicKeyRing.getSecretAuthenticationId(); + long authSubKeyId = mKeyRepository.getSecretAuthenticationId(masterKeyId); description += cachedPublicKeyRing.getPrimaryUserId(); description += " (" + Long.toHexString(authSubKeyId) + ")"; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RequestKeyPermissionPresenter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RequestKeyPermissionPresenter.java index 0644f6e95..4885ad8e5 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RequestKeyPermissionPresenter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RequestKeyPermissionPresenter.java @@ -123,7 +123,7 @@ class RequestKeyPermissionPresenter { } CachedPublicKeyRing cachedPublicKeyRing = keyRepository.getCachedPublicKeyRing(masterKeyId); - SecretKeyType secretKeyType = cachedPublicKeyRing.getSecretKeyType(candidateSubKeyId); + SecretKeyType secretKeyType = keyRepository.getSecretKeyType(candidateSubKeyId); if (secretKeyType.isUsable()) { return cachedPublicKeyRing; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java index fe9fc4987..d88e7b74d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java @@ -18,6 +18,8 @@ package org.sufficientlysecure.keychain.service; +import java.util.Date; + import android.app.AlarmManager; import android.app.Notification; import android.app.PendingIntent; @@ -41,14 +43,11 @@ import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants.NotificationIds; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType; -import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing; import org.sufficientlysecure.keychain.provider.KeyRepository; import org.sufficientlysecure.keychain.util.Passphrase; import org.sufficientlysecure.keychain.util.Preferences; import timber.log.Timber; -import java.util.Date; - /** * This service runs in its own process, but is available to all other processes as the main * passphrase cache. Use the static methods addCachedPassphrase and getCachedPassphrase for @@ -246,8 +245,8 @@ public class PassphraseCacheService extends Service { + masterKeyId + ", subKeyId " + subKeyId); // get the type of key (from the database) - CachedPublicKeyRing keyRing = KeyRepository.create(this).getCachedPublicKeyRing(masterKeyId); - SecretKeyType keyType = keyRing.getSecretKeyType(subKeyId); + KeyRepository keyRepository = KeyRepository.create(this); + SecretKeyType keyType = keyRepository.getSecretKeyType(subKeyId); switch (keyType) { case PASSPHRASE_EMPTY: diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ssh/AuthenticationOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ssh/AuthenticationOperation.java index 3df7f8724..31db064cf 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ssh/AuthenticationOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ssh/AuthenticationOperation.java @@ -17,8 +17,12 @@ package org.sufficientlysecure.keychain.ssh; + +import java.util.Collection; + import android.content.Context; import android.support.annotation.NonNull; + import org.bouncycastle.openpgp.AuthenticationSignatureGenerator; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.operator.jcajce.NfcSyncPGPContentSignerBuilder; @@ -29,15 +33,13 @@ import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey; import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing; import org.sufficientlysecure.keychain.pgp.PassphraseCacheInterface; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; -import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; import org.sufficientlysecure.keychain.provider.KeyRepository; +import org.sufficientlysecure.keychain.provider.KeyRepository.NotFoundException; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.service.input.RequiredInputParcel; import org.sufficientlysecure.keychain.util.Passphrase; import timber.log.Timber; -import java.util.Collection; - import static java.lang.String.format; @@ -101,8 +103,8 @@ public class AuthenticationOperation extends BaseOperation Long authSubKeyId = data.getAuthenticationSubKeyId(); if (authSubKeyId == null) { try { // Get the key id of the authentication key belonging to the master key id - authSubKeyId = mKeyRepository.getCachedPublicKeyRing(authMasterKeyId).getSecretAuthenticationId(); - } catch (PgpKeyNotFoundException e) { + authSubKeyId = mKeyRepository.getSecretAuthenticationId(authMasterKeyId); + } catch (NotFoundException e) { log.add(LogType.MSG_AUTH_ERROR_KEY_AUTH, indent); return new AuthenticationResult(AuthenticationResult.RESULT_ERROR, log); } @@ -142,9 +144,7 @@ public class AuthenticationOperation extends BaseOperation CanonicalizedSecretKey.SecretKeyType secretKeyType; try { - secretKeyType = mKeyRepository - .getCachedPublicKeyRing(authMasterKeyId) - .getSecretKeyType(authSubKeyId); + secretKeyType = mKeyRepository.getSecretKeyType(authSubKeyId); } catch (KeyRepository.NotFoundException e) { log.add(LogType.MSG_AUTH_ERROR_KEY_AUTH, indent); return new AuthenticationResult(AuthenticationResult.RESULT_ERROR, log); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java index 38577923a..5f31f882e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java @@ -36,9 +36,9 @@ import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.operations.results.CertifyResult; import org.sufficientlysecure.keychain.operations.results.OperationResult; -import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing; import org.sufficientlysecure.keychain.provider.KeyRepository; +import org.sufficientlysecure.keychain.provider.KeyRepository.NotFoundException; import org.sufficientlysecure.keychain.service.CertifyActionsParcel; import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyAction; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; @@ -75,7 +75,7 @@ public class CertifyKeyFragment if (key.canCertify()) { mCertifyKeySpinner.setPreSelectedKeyId(certifyKeyId); } - } catch (PgpKeyNotFoundException e) { + } catch (NotFoundException e) { Timber.e(e, "certify certify check failed"); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java index f1446a26d..a45c5777f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java @@ -48,9 +48,9 @@ import org.sufficientlysecure.keychain.operations.results.EditKeyResult; import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.operations.results.UploadResult; import org.sufficientlysecure.keychain.pgp.KeyRing; -import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing; import org.sufficientlysecure.keychain.provider.KeyRepository; +import org.sufficientlysecure.keychain.provider.KeyRepository.NotFoundException; import org.sufficientlysecure.keychain.service.ChangeUnlockParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyChange; @@ -413,10 +413,10 @@ public class CreateKeyFinalFragment extends Fragment { KeyRepository keyRepository = KeyRepository.create(getContext()); SaveKeyringParcel.Builder builder; - CachedPublicKeyRing key = keyRepository.getCachedPublicKeyRing(saveKeyResult.mMasterKeyId); try { + CachedPublicKeyRing key = keyRepository.getCachedPublicKeyRing(saveKeyResult.mMasterKeyId); builder = SaveKeyringParcel.buildChangeKeyringParcel(saveKeyResult.mMasterKeyId, key.getFingerprint()); - } catch (PgpKeyNotFoundException e) { + } catch (NotFoundException e) { Timber.e("Key that should be moved to Security Token not found in database!"); return; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DeleteKeyDialogActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DeleteKeyDialogActivity.java index a495deb9a..18d99741b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DeleteKeyDialogActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DeleteKeyDialogActivity.java @@ -38,6 +38,7 @@ import android.widget.Spinner; import android.widget.TextView; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.model.SubKey.UnifiedKeyInfo; import org.sufficientlysecure.keychain.operations.results.DeleteResult; import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.operations.results.RevokeResult; @@ -86,35 +87,23 @@ public class DeleteKeyDialogActivity extends FragmentActivity { if (mMasterKeyIds.length == 1 && mHasSecret) { // if mMasterKeyIds.length == 0 we let the DeleteOperation respond - try { - KeyRepository keyRepository = KeyRepository.create(this); - HashMap data = keyRepository.getUnifiedData( - mMasterKeyIds[0], new String[]{ - KeychainContract.KeyRings.NAME, - KeychainContract.KeyRings.IS_REVOKED - }, new int[]{ - KeyRepository.FIELD_TYPE_STRING, - KeyRepository.FIELD_TYPE_INTEGER - } - ); - - String name; - - name = (String) data.get(KeychainContract.KeyRings.NAME); - - if (name == null) { - name = getString(R.string.user_id_no_name); - } - - if ((long) data.get(KeychainContract.KeyRings.IS_REVOKED) > 0) { - showNormalDeleteDialog(); - } else { - showRevokeDeleteDialog(name); - } - } catch (KeyRepository.NotFoundException e) { - Timber.e(e, "Secret key to delete not found at DeleteKeyDialogActivity for " - + mMasterKeyIds[0]); + KeyRepository keyRepository = KeyRepository.create(this); + UnifiedKeyInfo keyInfo = keyRepository.getUnifiedKeyInfo(mMasterKeyIds[0]); + if (keyInfo == null) { + Timber.e("Secret key to delete not found at DeleteKeyDialogActivity for " + mMasterKeyIds[0]); finish(); + return; + } + + String name = keyInfo.name(); + if (name == null) { + name = getString(R.string.user_id_no_name); + } + + if (keyInfo.is_revoked()) { + showNormalDeleteDialog(); + } else { + showRevokeDeleteDialog(name); } } else { showNormalDeleteDialog(); @@ -269,36 +258,26 @@ public class DeleteKeyDialogActivity extends FragmentActivity { if (masterKeyIds.length == 1) { long masterKeyId = masterKeyIds[0]; - try { - HashMap data = KeyRepository.create(getContext()) - .getUnifiedData( - masterKeyId, new String[]{ - KeychainContract.KeyRings.NAME, - KeychainContract.KeyRings.HAS_ANY_SECRET - }, new int[]{ - KeyRepository.FIELD_TYPE_STRING, - KeyRepository.FIELD_TYPE_INTEGER - } - ); - String name; - - name = (String) data.get(KeychainContract.KeyRings.NAME); - if (name == null) { - name = getString(R.string.user_id_no_name); - } - - if (hasSecret) { - // show title only for secret key deletions, - // see http://www.google.com/design/spec/components/dialogs.html#dialogs-behavior - builder.setTitle(getString(R.string.title_delete_secret_key, name)); - mMainMessage.setText(getString(R.string.secret_key_deletion_confirmation, name)); - } else { - mMainMessage.setText(getString(R.string.public_key_deletetion_confirmation, name)); - } - } catch (KeyRepository.NotFoundException e) { + KeyRepository keyRepository = KeyRepository.create(getContext()); + UnifiedKeyInfo keyInfo = keyRepository.getUnifiedKeyInfo(masterKeyId); + if (keyInfo == null) { dismiss(); return null; } + String name = keyInfo.name(); + + if (name == null) { + name = getString(R.string.user_id_no_name); + } + + if (keyInfo.has_any_secret()) { + // show title only for secret key deletions, + // see http://www.google.com/design/spec/components/dialogs.html#dialogs-behavior + builder.setTitle(getString(R.string.title_delete_secret_key, name)); + mMainMessage.setText(getString(R.string.secret_key_deletion_confirmation, name)); + } else { + mMainMessage.setText(getString(R.string.public_key_deletetion_confirmation, name)); + } } else { mMainMessage.setText(R.string.key_deletion_confirmation_multi); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeAsymmetricFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeAsymmetricFragment.java index dd851052e..56cd80afd 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeAsymmetricFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeAsymmetricFragment.java @@ -32,7 +32,6 @@ import com.tokenautocomplete.TokenCompleteTextView.TokenListener; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing; -import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing; import org.sufficientlysecure.keychain.provider.KeyRepository; import org.sufficientlysecure.keychain.provider.KeyRepository.NotFoundException; @@ -142,7 +141,7 @@ public class EncryptModeAsymmetricFragment extends EncryptModeFragment { if (keyring.hasAnySecret()) { mSignKeySpinner.setPreSelectedKeyId(signatureKeyId); } - } catch (PgpKeyNotFoundException e) { + } catch (NotFoundException e) { Timber.e(e, "key not found for signing!"); Notify.create(getActivity(), getString(R.string.error_preselect_sign_key, KeyFormattingUtils.beautifyKeyId(signatureKeyId)), diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java index 4e48100ed..306bf0657 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java @@ -61,7 +61,6 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing; import org.sufficientlysecure.keychain.provider.KeyRepository; import org.sufficientlysecure.keychain.provider.KeyRepository.NotFoundException; -import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.service.PassphraseCacheService; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.service.input.RequiredInputParcel; @@ -115,10 +114,9 @@ public class PassphraseDialogActivity extends FragmentActivity { // handle empty passphrases by directly returning an empty crypto input parcel try { - CachedPublicKeyRing pubRing = - KeyRepository.create(this).getCachedPublicKeyRing(requiredInput.getMasterKeyId()); + KeyRepository keyRepository = KeyRepository.create(this); // use empty passphrase for empty passphrase - if (pubRing.getSecretKeyType(requiredInput.getSubKeyId()) == SecretKeyType.PASSPHRASE_EMPTY) { + if (keyRepository.getSecretKeyType(requiredInput.getSubKeyId()) == SecretKeyType.PASSPHRASE_EMPTY) { // also return passphrase back to activity Intent returnIntent = new Intent(); cryptoInputParcel = cryptoInputParcel.withPassphrase(new Passphrase(""), requiredInput.getSubKeyId()); @@ -299,7 +297,7 @@ public class PassphraseDialogActivity extends FragmentActivity { userId = getString(R.string.user_id_no_name); } - keyType = cachedPublicKeyRing.getSecretKeyType(subKeyId); + keyType = keyRepository.getSecretKeyType(subKeyId); switch (keyType) { case PASSPHRASE: message = getString(R.string.passphrase_for, userId); @@ -316,7 +314,7 @@ public class PassphraseDialogActivity extends FragmentActivity { throw new AssertionError("Unhandled SecretKeyType (should not happen)"); } } - } catch (PgpKeyNotFoundException | KeyRepository.NotFoundException e) { + } catch (KeyRepository.NotFoundException e) { alert.setTitle(R.string.title_key_not_found); alert.setMessage(getString(R.string.key_not_found, mRequiredInput.getSubKeyId())); alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/LinkedIdViewFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/LinkedIdViewFragment.java index 902003d41..5ef4d5358 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/LinkedIdViewFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/LinkedIdViewFragment.java @@ -54,8 +54,8 @@ import org.sufficientlysecure.keychain.livedata.GenericLiveData; import org.sufficientlysecure.keychain.model.Certification.CertDetails; import org.sufficientlysecure.keychain.operations.results.LinkedVerifyResult; import org.sufficientlysecure.keychain.operations.results.OperationResult; -import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; import org.sufficientlysecure.keychain.provider.KeyRepository; +import org.sufficientlysecure.keychain.provider.KeyRepository.NotFoundException; import org.sufficientlysecure.keychain.service.CertifyActionsParcel; import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyAction; import org.sufficientlysecure.keychain.ui.adapter.IdentityAdapter; @@ -382,9 +382,8 @@ public class LinkedIdViewFragment extends CryptoOperationFragment implements OnB byte[] fingerprint; try { - fingerprint = KeyRepository.create(activity).getCachedPublicKeyRing( - masterKeyId).getFingerprint(); - } catch (PgpKeyNotFoundException e) { + fingerprint = KeyRepository.create(activity).getCachedPublicKeyRing(masterKeyId).getFingerprint(); + } catch (NotFoundException e) { throw new IllegalStateException("Key to verify linked id for must exist in db!"); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/ViewKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/ViewKeyActivity.java index c69f91e26..7772202ca 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/ViewKeyActivity.java @@ -483,8 +483,7 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements private boolean keyHasPassphrase() { try { long masterKeyId = unifiedKeyInfo.master_key_id(); - SecretKeyType secretKeyType = - keyRepository.getCachedPublicKeyRing(masterKeyId).getSecretKeyType(masterKeyId); + SecretKeyType secretKeyType = keyRepository.getSecretKeyType(masterKeyId); switch (secretKeyType) { // all of these make no sense to ask case PASSPHRASE_EMPTY: diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/token/PublicKeyRetrievalLoader.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/token/PublicKeyRetrievalLoader.java index a0be35c87..767d446cf 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/token/PublicKeyRetrievalLoader.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/token/PublicKeyRetrievalLoader.java @@ -29,14 +29,12 @@ import android.os.SystemClock; import android.support.annotation.Nullable; import android.support.v4.content.AsyncTaskLoader; import android.text.TextUtils; -import android.util.Log; import com.google.auto.value.AutoValue; import okhttp3.Call; import okhttp3.HttpUrl; import okhttp3.Request.Builder; import okhttp3.Response; -import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.keyimport.HkpKeyserverAddress; import org.sufficientlysecure.keychain.keyimport.HkpKeyserverClient; import org.sufficientlysecure.keychain.keyimport.KeyserverClient.QueryFailedException; @@ -48,11 +46,8 @@ import org.sufficientlysecure.keychain.operations.results.OperationResult.Operat import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; import org.sufficientlysecure.keychain.pgp.UncachedKeyRing.IteratorWithIOThrow; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; -import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; -import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing; import org.sufficientlysecure.keychain.provider.KeyRepository; import org.sufficientlysecure.keychain.provider.KeyRepository.NotFoundException; -import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.ui.token.PublicKeyRetrievalLoader.KeyRetrievalResult; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.util.ParcelableProxy; @@ -120,7 +115,6 @@ public abstract class PublicKeyRetrievalLoader extends AsyncTaskLoader 1 AND ( expiry IS NULL OR expiry >= date('now') ) + AND can_sign = 1 AND master_key_id = ?; + +selectEffectiveAuthKeyIdByMasterKeyId: +SELECT key_id + FROM keys + WHERE is_revoked = 0 AND is_secure = 1 AND has_secret > 1 AND ( expiry IS NULL OR expiry >= date('now') ) + AND can_authenticate = 1 AND master_key_id = ?; + -- TODO move to KeySignatures.sq selectMasterKeyIdsBySigner: SELECT master_key_id - FROM key_signatures WHERE signer_key_id IN ?; \ No newline at end of file + FROM key_signatures WHERE signer_key_id IN ?; \ No newline at end of file diff --git a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/KeychainTestRunner.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/KeychainTestRunner.java index 31a1ebd3a..aad764af5 100644 --- a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/KeychainTestRunner.java +++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/KeychainTestRunner.java @@ -1,5 +1,6 @@ package org.sufficientlysecure.keychain; + import org.junit.runners.model.InitializationError; import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; diff --git a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/AuthenticationOperationTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/AuthenticationOperationTest.java index 8f3726895..17cea489d 100644 --- a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/AuthenticationOperationTest.java +++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/AuthenticationOperationTest.java @@ -19,6 +19,13 @@ package org.sufficientlysecure.keychain.operations; +import java.io.PrintStream; +import java.security.MessageDigest; +import java.security.PublicKey; +import java.security.Security; +import java.security.Signature; +import java.util.ArrayList; + import org.bouncycastle.bcpg.HashAlgorithmTags; import org.bouncycastle.jcajce.provider.asymmetric.eddsa.EdDSAEngine; import org.bouncycastle.jcajce.provider.asymmetric.eddsa.spec.EdDSANamedCurveTable; @@ -44,13 +51,6 @@ import org.sufficientlysecure.keychain.ssh.AuthenticationResult; import org.sufficientlysecure.keychain.support.KeyringTestingHelper; import org.sufficientlysecure.keychain.util.Passphrase; -import java.io.PrintStream; -import java.security.MessageDigest; -import java.security.PublicKey; -import java.security.Security; -import java.security.Signature; -import java.util.ArrayList; - @RunWith(KeychainTestRunner.class) public class AuthenticationOperationTest { @@ -160,7 +160,7 @@ public class AuthenticationOperationTest { KeyRepository keyRepository = KeyRepository.create(RuntimeEnvironment.application); long masterKeyId = mStaticRingRsa.getMasterKeyId(); - Long authSubKeyId = keyRepository.getCachedPublicKeyRing(masterKeyId).getSecretAuthenticationId(); + Long authSubKeyId = keyRepository.getSecretAuthenticationId(masterKeyId); { // sign challenge AuthenticationOperation op = new AuthenticationOperation(RuntimeEnvironment.application, @@ -206,7 +206,7 @@ public class AuthenticationOperationTest { KeyRepository keyRepository = KeyRepository.create(RuntimeEnvironment.application); long masterKeyId = mStaticRingEcDsa.getMasterKeyId(); - Long authSubKeyId = keyRepository.getCachedPublicKeyRing(masterKeyId).getSecretAuthenticationId(); + Long authSubKeyId = keyRepository.getSecretAuthenticationId(masterKeyId); { // sign challenge AuthenticationOperation op = new AuthenticationOperation(RuntimeEnvironment.application, @@ -252,7 +252,7 @@ public class AuthenticationOperationTest { KeyRepository keyRepository = KeyRepository.create(RuntimeEnvironment.application); long masterKeyId = mStaticRingEdDsa.getMasterKeyId(); - Long authSubKeyId = keyRepository.getCachedPublicKeyRing(masterKeyId).getSecretAuthenticationId(); + Long authSubKeyId = keyRepository.getSecretAuthenticationId(masterKeyId); { // sign challenge AuthenticationOperation op = new AuthenticationOperation(RuntimeEnvironment.application, @@ -300,7 +300,7 @@ public class AuthenticationOperationTest { KeyRepository keyRepository = KeyRepository.create(RuntimeEnvironment.application); long masterKeyId = mStaticRingDsa.getMasterKeyId(); - Long authSubKeyId = keyRepository.getCachedPublicKeyRing(masterKeyId).getSecretAuthenticationId(); + Long authSubKeyId = keyRepository.getSecretAuthenticationId(masterKeyId); { // sign challenge AuthenticationOperation op = new AuthenticationOperation(RuntimeEnvironment.application, @@ -345,7 +345,7 @@ public class AuthenticationOperationTest { KeyRepository keyRepository = KeyRepository.create(RuntimeEnvironment.application); long masterKeyId = mStaticRingEcDsa.getMasterKeyId(); - Long authSubKeyId = keyRepository.getCachedPublicKeyRing(masterKeyId).getSecretAuthenticationId(); + Long authSubKeyId = keyRepository.getSecretAuthenticationId(masterKeyId); { // sign challenge - should succeed with selected key allowed AuthenticationOperation op = new AuthenticationOperation(RuntimeEnvironment.application, diff --git a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/BackupOperationTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/BackupOperationTest.java index 7417d4314..1dd1b69bb 100644 --- a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/BackupOperationTest.java +++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/BackupOperationTest.java @@ -162,15 +162,6 @@ public class BackupOperationTest { assertTrue("export must be a success", result); - long masterKeyId1, masterKeyId2; - if (mStaticRing1.getMasterKeyId() < mStaticRing2.getMasterKeyId()) { - masterKeyId1 = mStaticRing1.getMasterKeyId(); - masterKeyId2 = mStaticRing2.getMasterKeyId(); - } else { - masterKeyId2 = mStaticRing1.getMasterKeyId(); - masterKeyId1 = mStaticRing2.getMasterKeyId(); - } - IteratorWithIOThrow unc = UncachedKeyRing.fromStream(new ByteArrayInputStream(out.toByteArray())); @@ -178,7 +169,7 @@ public class BackupOperationTest { assertTrue("export must have two keys (1/2)", unc.hasNext()); UncachedKeyRing ring = unc.next(); Assert.assertEquals("first exported key has correct masterkeyid", - masterKeyId1, ring.getMasterKeyId()); + mStaticRing2.getMasterKeyId(), ring.getMasterKeyId()); assertFalse("first exported key must not be secret", ring.isSecret()); assertFalse("there must be no local signatures in an exported keyring", checkForLocal(ring)); @@ -188,7 +179,7 @@ public class BackupOperationTest { assertTrue("export must have two keys (2/2)", unc.hasNext()); UncachedKeyRing ring = unc.next(); Assert.assertEquals("second exported key has correct masterkeyid", - masterKeyId2, ring.getMasterKeyId()); + mStaticRing1.getMasterKeyId(), ring.getMasterKeyId()); assertFalse("second exported key must not be secret", ring.isSecret()); assertFalse("there must be no local signatures in an exported keyring", checkForLocal(ring)); @@ -205,7 +196,7 @@ public class BackupOperationTest { assertTrue("export must have four keys (1/4)", unc.hasNext()); UncachedKeyRing ring = unc.next(); Assert.assertEquals("1/4 exported key has correct masterkeyid", - masterKeyId1, ring.getMasterKeyId()); + mStaticRing2.getMasterKeyId(), ring.getMasterKeyId()); assertFalse("1/4 exported key must not be public", ring.isSecret()); assertFalse("there must be no local signatures in an exported keyring", checkForLocal(ring)); @@ -213,7 +204,7 @@ public class BackupOperationTest { assertTrue("export must have four keys (2/4)", unc.hasNext()); ring = unc.next(); Assert.assertEquals("2/4 exported key has correct masterkeyid", - masterKeyId1, ring.getMasterKeyId()); + mStaticRing2.getMasterKeyId(), ring.getMasterKeyId()); assertTrue("2/4 exported key must be public", ring.isSecret()); assertFalse("there must be no local signatures in an exported keyring", checkForLocal(ring)); @@ -223,7 +214,7 @@ public class BackupOperationTest { assertTrue("export must have four keys (3/4)", unc.hasNext()); UncachedKeyRing ring = unc.next(); Assert.assertEquals("3/4 exported key has correct masterkeyid", - masterKeyId2, ring.getMasterKeyId()); + mStaticRing1.getMasterKeyId(), ring.getMasterKeyId()); assertFalse("3/4 exported key must not be public", ring.isSecret()); assertFalse("there must be no local signatures in an exported keyring", checkForLocal(ring)); @@ -231,7 +222,7 @@ public class BackupOperationTest { assertTrue("export must have four keys (4/4)", unc.hasNext()); ring = unc.next(); Assert.assertEquals("4/4 exported key has correct masterkeyid", - masterKeyId2, ring.getMasterKeyId()); + mStaticRing1.getMasterKeyId(), ring.getMasterKeyId()); assertTrue("4/4 exported key must be public", ring.isSecret()); assertFalse("there must be no local signatures in an exported keyring", checkForLocal(ring)); diff --git a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/CertifyOperationTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/CertifyOperationTest.java index e28c1fc10..89ca8c060 100644 --- a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/CertifyOperationTest.java +++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/CertifyOperationTest.java @@ -149,8 +149,8 @@ public class CertifyOperationTest { { CanonicalizedPublicKeyRing ring = KeyWritableRepository.create(RuntimeEnvironment.application) .getCanonicalizedPublicKeyRing(mStaticRing2.getMasterKeyId()); - Assert.assertEquals("public key must not be marked verified prior to certification", - VerificationStatus.UNVERIFIED, ring.getVerified()); + Assert.assertNull("public key must not be marked verified prior to certification", + ring.getVerified()); } CertifyActionsParcel.Builder actions = CertifyActionsParcel.builder(mStaticRing1.getMasterKeyId()); @@ -164,21 +164,20 @@ public class CertifyOperationTest { CanonicalizedPublicKeyRing ring = KeyWritableRepository.create(RuntimeEnvironment.application) .getCanonicalizedPublicKeyRing(mStaticRing2.getMasterKeyId()); Assert.assertEquals("new key must be verified now", - VerificationStatus.UNVERIFIED, ring.getVerified()); + VerificationStatus.VERIFIED_SECRET, ring.getVerified()); } } @Test public void testCertifyAttribute() throws Exception { - CertifyOperation op = new CertifyOperation(RuntimeEnvironment.application, - KeyWritableRepository.create(RuntimeEnvironment.application), null, null); + KeyWritableRepository keyWritableRepository = KeyWritableRepository.create(RuntimeEnvironment.application); + CertifyOperation op = new CertifyOperation(RuntimeEnvironment.application, keyWritableRepository, null, null); { - CanonicalizedPublicKeyRing ring = KeyWritableRepository.create(RuntimeEnvironment.application) - .getCanonicalizedPublicKeyRing(mStaticRing2.getMasterKeyId()); - Assert.assertEquals("public key must not be marked verified prior to certification", - VerificationStatus.UNVERIFIED, ring.getVerified()); + CanonicalizedPublicKeyRing ring = keyWritableRepository.getCanonicalizedPublicKeyRing(mStaticRing2.getMasterKeyId()); + Assert.assertNull("public key must not be marked verified prior to certification", + ring.getVerified()); } CertifyActionsParcel.Builder actions = CertifyActionsParcel.builder(mStaticRing1.getMasterKeyId()); @@ -189,8 +188,7 @@ public class CertifyOperationTest { Assert.assertTrue("certification must succeed", result.success()); { - CanonicalizedPublicKeyRing ring = KeyWritableRepository.create(RuntimeEnvironment.application) - .getCanonicalizedPublicKeyRing(mStaticRing2.getMasterKeyId()); + CanonicalizedPublicKeyRing ring = keyWritableRepository.getCanonicalizedPublicKeyRing(mStaticRing2.getMasterKeyId()); Assert.assertEquals("new key must be verified now", VerificationStatus.VERIFIED_SECRET, ring.getVerified()); } diff --git a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperationTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperationTest.java index 033908a44..db0fa2a5c 100644 --- a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperationTest.java +++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperationTest.java @@ -105,8 +105,9 @@ public class PromoteKeyOperationTest { @Test public void testPromote() throws Exception { + KeyWritableRepository keyRepository = KeyWritableRepository.create(RuntimeEnvironment.application); PromoteKeyOperation op = new PromoteKeyOperation(RuntimeEnvironment.application, - KeyWritableRepository.create(RuntimeEnvironment.application), null, null); + keyRepository, null, null); PromoteKeyResult result = op.execute( PromoteKeyringParcel.createPromoteKeyringParcel(mStaticRing.getMasterKeyId(), null, null), null); @@ -114,15 +115,14 @@ public class PromoteKeyOperationTest { Assert.assertTrue("promotion must succeed", result.success()); { - CachedPublicKeyRing ring = KeyWritableRepository.create(RuntimeEnvironment.application) - .getCachedPublicKeyRing(mStaticRing.getMasterKeyId()); + CachedPublicKeyRing ring = keyRepository.getCachedPublicKeyRing(mStaticRing.getMasterKeyId()); Assert.assertTrue("key must have a secret now", ring.hasAnySecret()); Iterator it = mStaticRing.getPublicKeys(); while (it.hasNext()) { long keyId = it.next().getKeyId(); Assert.assertEquals("all subkeys must be gnu dummy", - SecretKeyType.GNU_DUMMY, ring.getSecretKeyType(keyId)); + SecretKeyType.GNU_DUMMY, keyRepository.getSecretKeyType(keyId)); } } diff --git a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/PgpEncryptDecryptTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/PgpEncryptDecryptTest.java index 0a6f03932..39deab677 100644 --- a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/PgpEncryptDecryptTest.java +++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/PgpEncryptDecryptTest.java @@ -36,6 +36,7 @@ import org.bouncycastle.bcpg.PublicKeyEncSessionPacket; import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags; import org.bouncycastle.bcpg.sig.KeyFlags; import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.BeforeClass; @@ -54,7 +55,7 @@ import org.sufficientlysecure.keychain.pgp.SecurityProblem.InsecureBitStrength; import org.sufficientlysecure.keychain.pgp.SecurityProblem.InsecureEncryptionAlgorithm; import org.sufficientlysecure.keychain.pgp.SecurityProblem.MissingMdc; import org.sufficientlysecure.keychain.provider.KeyWritableRepository; -import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData; +import org.sufficientlysecure.keychain.provider.KeychainDatabase; import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyChange; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; diff --git a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperationTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperationTest.java index c96cc1ea2..37108971a 100644 --- a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperationTest.java +++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperationTest.java @@ -55,6 +55,7 @@ import org.sufficientlysecure.keychain.KeychainTestRunner; import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType; import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; import org.sufficientlysecure.keychain.operations.results.PgpEditKeyResult; +import org.sufficientlysecure.keychain.pgp.CanonicalizedKeyRing.VerificationStatus; import org.sufficientlysecure.keychain.service.ChangeUnlockParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm; @@ -672,7 +673,7 @@ public class PgpKeyOperationTest { resetBuilder(); builder.addRevokeSubkey(123L); - CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), 0); + CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), VerificationStatus.UNVERIFIED); UncachedKeyRing otherModified = op.modifySecretKeyRing(secretRing, cryptoInput, builder.build()).getRing(); Assert.assertNull("revoking a nonexistent subkey should fail", otherModified); @@ -869,7 +870,7 @@ public class PgpKeyOperationTest { securityTokenBuilder.addOrReplaceSubkeyChange(SubkeyChange.createMoveToSecurityTokenChange(keyId)); CanonicalizedSecretKeyRing secretRing = - new CanonicalizedSecretKeyRing(ringSecurityToken.getEncoded(), 0); + new CanonicalizedSecretKeyRing(ringSecurityToken.getEncoded(), VerificationStatus.UNVERIFIED); PgpKeyOperation op = new PgpKeyOperation(null); PgpEditKeyResult result = op.modifySecretKeyRing(secretRing, cryptoInput, securityTokenBuilder.build()); Assert.assertTrue("moveKeyToSecurityToken operation should be pending", result.isPending()); @@ -904,7 +905,7 @@ public class PgpKeyOperationTest { securityTokenBuilder.addOrReplaceSubkeyChange(SubkeyChange.createRecertifyChange(keyId, true)); CanonicalizedSecretKeyRing secretRing = - new CanonicalizedSecretKeyRing(modified.getEncoded(), 0); + new CanonicalizedSecretKeyRing(modified.getEncoded(), VerificationStatus.UNVERIFIED); PgpKeyOperation op = new PgpKeyOperation(null); PgpEditKeyResult result = op.modifySecretKeyRing(secretRing, cryptoInput, securityTokenBuilder.build()); Assert.assertTrue("moveKeyToSecurityToken operation should be pending", result.isPending()); @@ -1187,7 +1188,7 @@ public class PgpKeyOperationTest { // we should still be able to modify it (and change its passphrase) without errors PgpKeyOperation op = new PgpKeyOperation(null); - CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(modified.getEncoded(), 0); + CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(modified.getEncoded(), VerificationStatus.UNVERIFIED); PgpEditKeyResult result = op.modifySecretKeyRing(secretRing, otherCryptoInput, builder.build()); Assert.assertTrue("key modification must succeed", result.success()); Assert.assertFalse("log must not contain a warning", @@ -1201,7 +1202,7 @@ public class PgpKeyOperationTest { modified = KeyringTestingHelper.injectPacket(modified, sKeyWithPassphrase.buf, sKeyWithPassphrase.position); PgpKeyOperation op = new PgpKeyOperation(null); - CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(modified.getEncoded(), 0); + CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(modified.getEncoded(), VerificationStatus.UNVERIFIED); PgpEditKeyResult result = op.modifySecretKeyRing(secretRing, CryptoInputParcel.createCryptoInputParcel(otherPassphrase2), builder.build()); Assert.assertTrue("key modification must succeed", result.success()); @@ -1214,7 +1215,7 @@ public class PgpKeyOperationTest { @Test public void testRestricted() throws Exception { - CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), 0); + CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), VerificationStatus.UNVERIFIED); builder.addUserId("discord"); PgpKeyOperation op = new PgpKeyOperation(null); @@ -1250,7 +1251,7 @@ public class PgpKeyOperationTest { try { Assert.assertTrue("modified keyring must be secret", ring.isSecret()); - CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), 0); + CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), VerificationStatus.UNVERIFIED); PgpKeyOperation op = new PgpKeyOperation(null); PgpEditKeyResult result = op.modifySecretKeyRing(secretRing, cryptoInput, parcel); @@ -1323,7 +1324,7 @@ public class PgpKeyOperationTest { SaveKeyringParcel parcel, CryptoInputParcel cryptoInput, LogType expected) throws Exception { - CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), 0); + CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), VerificationStatus.UNVERIFIED); PgpEditKeyResult result = op.modifySecretKeyRing(secretRing, cryptoInput, parcel); Assert.assertFalse(reason, result.success()); @@ -1337,7 +1338,7 @@ public class PgpKeyOperationTest { LogType expected) throws Exception { - CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), 0); + CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), VerificationStatus.UNVERIFIED); PgpEditKeyResult result = op.modifySecretKeyRing(secretRing, cryptoInput, parcel); Assert.assertFalse(reason, result.success()); diff --git a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringMergeTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringMergeTest.java index abc71cd65..22beb189c 100644 --- a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringMergeTest.java +++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringMergeTest.java @@ -43,6 +43,7 @@ import org.sufficientlysecure.keychain.KeychainTestRunner; import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; import org.sufficientlysecure.keychain.operations.results.PgpEditKeyResult; +import org.sufficientlysecure.keychain.pgp.CanonicalizedKeyRing.VerificationStatus; import org.sufficientlysecure.keychain.pgp.PgpCertifyOperation.PgpCertifyResult; import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyAction; import org.sufficientlysecure.keychain.service.ChangeUnlockParcel; @@ -188,7 +189,7 @@ public class UncachedKeyringMergeTest { UncachedKeyRing modifiedA, modifiedB; { CanonicalizedSecretKeyRing secretRing = - new CanonicalizedSecretKeyRing(ringA.getEncoded(), 0); + new CanonicalizedSecretKeyRing(ringA.getEncoded(), VerificationStatus.UNVERIFIED); resetBuilder(); builder.addUserId("flim"); @@ -230,7 +231,7 @@ public class UncachedKeyringMergeTest { UncachedKeyRing modifiedA, modifiedB; long subKeyIdA, subKeyIdB; { - CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ringA.getEncoded(), 0); + CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ringA.getEncoded(), VerificationStatus.UNVERIFIED); resetBuilder(); builder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd( @@ -278,7 +279,7 @@ public class UncachedKeyringMergeTest { resetBuilder(); builder.addRevokeSubkey(KeyringTestingHelper.getSubkeyId(ringA, 1)); CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing( - ringA.getEncoded(), 0); + ringA.getEncoded(), VerificationStatus.UNVERIFIED); modified = op.modifySecretKeyRing(secretRing, CryptoInputParcel.createCryptoInputParcel(new Date(), new Passphrase()), builder.build()).getRing(); } @@ -301,10 +302,10 @@ public class UncachedKeyringMergeTest { final UncachedKeyRing modified; { CanonicalizedPublicKeyRing publicRing = new CanonicalizedPublicKeyRing( - pubRing.getEncoded(), 0); + pubRing.getEncoded(), VerificationStatus.UNVERIFIED); CanonicalizedSecretKey secretKey = new CanonicalizedSecretKeyRing( - ringB.getEncoded(), 0).getSecretKey(); + ringB.getEncoded(), VerificationStatus.UNVERIFIED).getSecretKey(); secretKey.unlock(new Passphrase()); PgpCertifyOperation op = new PgpCertifyOperation(); CertifyAction action = CertifyAction.createForUserIds( @@ -379,7 +380,7 @@ public class UncachedKeyringMergeTest { builder.addUserAttribute(uat); CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing( - ringA.getEncoded(), 0); + ringA.getEncoded(), VerificationStatus.UNVERIFIED); modified = op.modifySecretKeyRing(secretRing, CryptoInputParcel.createCryptoInputParcel(new Date(), new Passphrase()), builder.build()).getRing(); } diff --git a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/provider/InteropTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/provider/InteropTest.java index abb9eaf99..9724a5fc6 100644 --- a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/provider/InteropTest.java +++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/provider/InteropTest.java @@ -27,8 +27,6 @@ import java.net.URL; import java.security.Security; import java.util.ArrayList; -import android.net.Uri; - import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.json.JSONArray; import org.json.JSONObject; @@ -45,14 +43,9 @@ import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult; import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; import org.sufficientlysecure.keychain.pgp.CanonicalizedKeyRing; import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKey; -import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing; -import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType; -import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing; import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel; import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyOperation; import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; -import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; -import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.util.InputData; @@ -62,7 +55,7 @@ import org.sufficientlysecure.keychain.util.Passphrase; public class InteropTest { @BeforeClass - public static void setUpOnce() throws Exception { + public static void setUpOnce() { Security.insertProviderAt(new BouncyCastleProvider(), 1); ShadowLog.stream = System.out; } @@ -104,11 +97,11 @@ public class InteropTest { } } - private static final String asString(File json) throws Exception { + private static String asString(File json) throws Exception { return new String(asBytes(json), "utf-8"); } - private static final byte[] asBytes(File f) throws Exception { + private static byte[] asBytes(File f) throws Exception { FileInputStream fin = null; try { fin = new FileInputStream(f); @@ -123,16 +116,14 @@ public class InteropTest { private void runDecryptTest(JSONObject config, File base) throws Exception { File root = base.getParentFile(); String baseName = getBaseName(base); - CanonicalizedPublicKeyRing verify; + UncachedKeyRing verify; if (config.has("verifyKey")) { - verify = (CanonicalizedPublicKeyRing) - readRingFromFile(new File(root, config.getString("verifyKey"))); + verify = readUncachedRingFromFile(new File(root, config.getString("verifyKey"))); } else { verify = null; } - CanonicalizedSecretKeyRing decrypt = (CanonicalizedSecretKeyRing) - readRingFromFile(new File(root, config.getString("decryptKey"))); + UncachedKeyRing decrypt = readUncachedRingFromFile(new File(root, config.getString("decryptKey"))); ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayInputStream in = @@ -154,9 +145,10 @@ public class InteropTest { if (verify != null) { // Certain keys are too short, so we check appropriately. int code = result.getSignatureResult().getResult(); - Assert.assertTrue(base + ": should have a signature", - (code == OpenPgpSignatureResult.RESULT_INVALID_KEY_INSECURE) || - (code == OpenPgpSignatureResult.RESULT_VALID_KEY_UNCONFIRMED)); + Assert.assertTrue(base + ": should have a signature (code: " + code + ")", + code == OpenPgpSignatureResult.RESULT_INVALID_KEY_INSECURE || + code == OpenPgpSignatureResult.RESULT_VALID_KEY_UNCONFIRMED + || code == OpenPgpSignatureResult.RESULT_VALID_KEY_CONFIRMED); } OpenPgpMetadata metadata = result.getDecryptionMetadata(); Assert.assertEquals(base + ": filesize must be correct", @@ -168,11 +160,10 @@ public class InteropTest { private void runImportTest(JSONObject config, File base) throws Exception { File root = base.getParentFile(); String baseName = getBaseName(base); - CanonicalizedKeyRing pkr = - readRingFromFile(new File(root, baseName + ".asc")); + CanonicalizedKeyRing pkr = readRingFromFile(new File(root, baseName + ".asc")); // Check we have the correct uids. - ArrayList expected = new ArrayList(); + ArrayList expected = new ArrayList<>(); JSONArray uids = config.getJSONArray("expected_uids"); for (int i = 0; i < uids.length(); i++) { expected.add(uids.getString(i)); @@ -188,7 +179,7 @@ public class InteropTest { expected.add(subkeys.getJSONObject(i).getString("expected_fingerprint")); } } - ArrayList actual = new ArrayList(); + ArrayList actual = new ArrayList<>(); for (CanonicalizedPublicKey pk: pkr.publicKeyIterator()) { if (pk.isValid()) { actual.add(KeyFormattingUtils.convertFingerprintToHex(pk.getFingerprint())); @@ -204,7 +195,7 @@ public class InteropTest { } } - UncachedKeyRing readUncachedRingFromFile(File path) throws Exception { + private UncachedKeyRing readUncachedRingFromFile(File path) throws Exception { BufferedInputStream bin = null; try { bin = new BufferedInputStream(new FileInputStream(path)); @@ -214,87 +205,40 @@ public class InteropTest { } } - CanonicalizedKeyRing readRingFromFile(File path) throws Exception { + private CanonicalizedKeyRing readRingFromFile(File path) throws Exception { UncachedKeyRing ukr = readUncachedRingFromFile(path); OperationLog log = new OperationLog(); return ukr.canonicalize(log, 0); } - private static final void close(Closeable v) { + private static void close(Closeable v) { if (v != null) { try { v.close(); - } catch (Throwable any) { + } catch (Throwable ignored) { } } } - private static final String getBaseName(File base) { + private static String getBaseName(File base) { String name = base.getName(); return name.substring(0, name.length() - ".json".length()); } private PgpDecryptVerifyOperation makeOperation(final String msg, final Passphrase passphrase, - final CanonicalizedSecretKeyRing decrypt, final CanonicalizedPublicKeyRing verify) - throws Exception { + UncachedKeyRing decrypt, UncachedKeyRing verify) { + KeyWritableRepository keyRepository = KeyWritableRepository.create(RuntimeEnvironment.application); - final long decryptId = decrypt.getEncryptId(); - final Uri decryptUri = KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(decryptId); - final Uri verifyUri = verify != null ? - KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(verify.getMasterKeyId()) : null; - - KeyWritableRepository helper = new KeyWritableRepository(RuntimeEnvironment.application, - KeychainDatabase.getInstance(RuntimeEnvironment.application), - LocalPublicKeyStorage.getInstance(RuntimeEnvironment.application), - LocalSecretKeyStorage.getInstance(RuntimeEnvironment.application), - DatabaseNotifyManager.create(RuntimeEnvironment.application), - AutocryptPeerDao.getInstance(RuntimeEnvironment.application)) { + Assert.assertTrue(keyRepository.saveSecretKeyRing(decrypt).success()); + if (verify != null) { + Assert.assertTrue(keyRepository.savePublicKeyRing(verify).success()); + } + return new PgpDecryptVerifyOperation(RuntimeEnvironment.application, keyRepository, null) { @Override - public CachedPublicKeyRing getCachedPublicKeyRing(Uri queryUri) throws PgpKeyNotFoundException { - Assert.assertEquals(msg + ": query should be for the decryption key", queryUri, decryptUri); - return new CachedPublicKeyRing(this, queryUri) { - @Override - public long getMasterKeyId() throws PgpKeyNotFoundException { - return decrypt.getMasterKeyId(); - } - - @Override - public SecretKeyType getSecretKeyType(long keyId) throws NotFoundException { - return decrypt.getSecretKey(keyId).getSecretKeyTypeSuperExpensive(); - } - }; - } - - public CanonicalizedPublicKeyRing getCanonicalizedPublicKeyRing(Uri q) - throws NotFoundException { - Assert.assertEquals(msg + ": query should be for verification key", q, verifyUri); - return verify; - } - - public CanonicalizedSecretKeyRing getCanonicalizedSecretKeyRing(Uri q) - throws NotFoundException { - Assert.assertEquals(msg + ": query should be for the decryption key", q, decryptUri); - return decrypt; - } - - @Override - public CanonicalizedSecretKeyRing getCanonicalizedSecretKeyRing(long masterKeyId) - throws NotFoundException { - Assert.assertEquals(msg + ": query should be for the decryption key", - masterKeyId, decrypt.getMasterKeyId()); - return decrypt; - } - }; - - return new PgpDecryptVerifyOperation(RuntimeEnvironment.application, helper, null) { - @Override - public Passphrase getCachedPassphrase(long masterKeyId, long subKeyId) - throws NoSecretKeyException { + public Passphrase getCachedPassphrase(long masterKeyId, long subKeyId) { Assert.assertEquals(msg + ": passphrase should be for the secret key", masterKeyId, decrypt.getMasterKeyId()); - Assert.assertEquals(msg + ": passphrase should refer to the decryption subkey", - subKeyId, decryptId); return passphrase; } }; diff --git a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/provider/KeyRepositorySaveTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/provider/KeyRepositorySaveTest.java index c4edf93c1..9941a380b 100644 --- a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/provider/KeyRepositorySaveTest.java +++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/provider/KeyRepositorySaveTest.java @@ -18,6 +18,10 @@ package org.sufficientlysecure.keychain.provider; + +import java.util.Arrays; +import java.util.Iterator; + import org.bouncycastle.bcpg.sig.KeyFlags; import org.bouncycastle.util.encoders.Hex; import org.junit.Assert; @@ -36,9 +40,6 @@ import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing; import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; import org.sufficientlysecure.keychain.util.IterableIterator; -import java.util.Arrays; -import java.util.Iterator; - @RunWith(KeychainTestRunner.class) public class KeyRepositorySaveTest { @@ -153,9 +154,8 @@ public class KeyRepositorySaveTest { Assert.assertTrue("canCertify() should be true", key.canCertify()); Assert.assertTrue("canSign() should be true", key.canSign()); - // cached Assert.assertEquals("all subkeys from CachedPublicKeyRing should be divert-to-key", - SecretKeyType.DIVERT_TO_CARD, cachedRing.getSecretKeyType(key.getKeyId())); + SecretKeyType.DIVERT_TO_CARD, mDatabaseInteractor.getSecretKeyType(key.getKeyId())); } { // second subkey @@ -169,7 +169,7 @@ public class KeyRepositorySaveTest { // cached Assert.assertEquals("all subkeys from CachedPublicKeyRing should be divert-to-key", - SecretKeyType.DIVERT_TO_CARD, cachedRing.getSecretKeyType(key.getKeyId())); + SecretKeyType.DIVERT_TO_CARD, mDatabaseInteractor.getSecretKeyType(key.getKeyId())); } { // third subkey @@ -183,7 +183,7 @@ public class KeyRepositorySaveTest { // cached Assert.assertEquals("all subkeys from CachedPublicKeyRing should be divert-to-key", - SecretKeyType.DIVERT_TO_CARD, cachedRing.getSecretKeyType(key.getKeyId())); + SecretKeyType.DIVERT_TO_CARD, mDatabaseInteractor.getSecretKeyType(key.getKeyId())); } Assert.assertFalse("keyring should have 3 subkeys (4)", it.hasNext()); @@ -233,14 +233,14 @@ public class KeyRepositorySaveTest { Assert.assertTrue("master key should have sign flag", ring.getPublicKey().canSign()); Assert.assertTrue("master key should have encrypt flag", ring.getPublicKey().canEncrypt()); - signId = mDatabaseInteractor.getCachedPublicKeyRing(masterKeyId).getSecretSignId(); + signId = mDatabaseInteractor.getSecretSignId(masterKeyId); Assert.assertNotEquals("encrypt id should not be 0", 0, signId); - Assert.assertNotEquals("encrypt key should be different from master key", masterKeyId, signId); + Assert.assertNotEquals("signing key should be different from master key", masterKeyId, signId); } { - CachedPublicKeyRing ring = mDatabaseInteractor.getCachedPublicKeyRing(masterKeyId); - Assert.assertEquals("signing key should be same id cached as uncached", signId, ring.getSecretSignId()); + Assert.assertEquals("signing key should be same id cached as uncached", + signId, mDatabaseInteractor.getSecretSignId(masterKeyId)); } } diff --git a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/ssh/SshPublicKeyTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/ssh/SshPublicKeyTest.java index 088105ffc..5b247c4fc 100644 --- a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/ssh/SshPublicKeyTest.java +++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/ssh/SshPublicKeyTest.java @@ -76,7 +76,7 @@ public class SshPublicKeyTest { KeyRepository keyRepository = KeyRepository.create(RuntimeEnvironment.application); long masterKeyId = mStaticRingEcDsa.getMasterKeyId(); - long authSubKeyId = keyRepository.getCachedPublicKeyRing(masterKeyId).getSecretAuthenticationId(); + long authSubKeyId = keyRepository.getSecretAuthenticationId(masterKeyId); CanonicalizedPublicKey canonicalizedPublicKey = keyRepository.getCanonicalizedPublicKeyRing(masterKeyId) .getPublicKey(authSubKeyId);