Get rid of more places where URIs are used for loading
This commit is contained in:
@@ -1,120 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2015 Vincent Breitmoser <look@my.amazin.horse>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.sufficientlysecure.keychain.ui;
|
|
||||||
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.app.Instrumentation.ActivityResult;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.support.test.espresso.intent.rule.IntentsTestRule;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.FixMethodOrder;
|
|
||||||
import org.junit.Rule;
|
|
||||||
import org.junit.runners.MethodSorters;
|
|
||||||
import org.sufficientlysecure.keychain.R;
|
|
||||||
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
|
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
|
||||||
import org.sufficientlysecure.keychain.provider.TemporaryFileProvider;
|
|
||||||
import org.sufficientlysecure.keychain.ui.util.Notify.Style;
|
|
||||||
|
|
||||||
import static android.support.test.espresso.Espresso.onView;
|
|
||||||
import static android.support.test.espresso.action.ViewActions.click;
|
|
||||||
import static android.support.test.espresso.intent.Intents.intending;
|
|
||||||
import static android.support.test.espresso.intent.matcher.IntentMatchers.hasAction;
|
|
||||||
import static android.support.test.espresso.intent.matcher.IntentMatchers.hasExtra;
|
|
||||||
import static android.support.test.espresso.intent.matcher.IntentMatchers.hasType;
|
|
||||||
import static android.support.test.espresso.intent.matcher.UriMatchers.hasHost;
|
|
||||||
import static android.support.test.espresso.intent.matcher.UriMatchers.hasScheme;
|
|
||||||
import static android.support.test.espresso.matcher.ViewMatchers.assertThat;
|
|
||||||
import static android.support.test.espresso.matcher.ViewMatchers.withId;
|
|
||||||
import static org.hamcrest.CoreMatchers.allOf;
|
|
||||||
import static org.hamcrest.CoreMatchers.startsWith;
|
|
||||||
import static org.hamcrest.Matchers.equalTo;
|
|
||||||
import static org.hamcrest.Matchers.is;
|
|
||||||
import static org.sufficientlysecure.keychain.AndroidTestHelpers.checkAndDismissSnackbar;
|
|
||||||
import static org.sufficientlysecure.keychain.AndroidTestHelpers.cleanupForTests;
|
|
||||||
|
|
||||||
//TODO This test is disabled because it needs to be fixed to work with updated code
|
|
||||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
|
||||||
//@RunWith(AndroidJUnit4.class)
|
|
||||||
//@LargeTest
|
|
||||||
public class ViewKeyAdvShareTest {
|
|
||||||
|
|
||||||
@Rule
|
|
||||||
public final IntentsTestRule<ViewKeyAdvActivity> mActivityRule
|
|
||||||
= new IntentsTestRule<ViewKeyAdvActivity>(ViewKeyAdvActivity.class) {
|
|
||||||
@Override
|
|
||||||
protected Intent getActivityIntent() {
|
|
||||||
Intent intent = super.getActivityIntent();
|
|
||||||
intent.setData(KeyRings.buildGenericKeyRingUri(0x9D604D2F310716A3L));
|
|
||||||
intent.putExtra(ViewKeyAdvActivity.EXTRA_SELECTED_TAB, ViewKeyAdvActivity.TAB_SHARE);
|
|
||||||
return intent;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
private Activity mActivity;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() throws Exception {
|
|
||||||
mActivity = mActivityRule.getActivity();
|
|
||||||
|
|
||||||
cleanupForTests(mActivity);
|
|
||||||
}
|
|
||||||
|
|
||||||
//@Test
|
|
||||||
public void testShareOperations() throws Exception {
|
|
||||||
|
|
||||||
// no-op should yield snackbar
|
|
||||||
onView(withId(R.id.view_key_action_fingerprint_clipboard)).perform(click());
|
|
||||||
checkAndDismissSnackbar(Style.OK, R.string.fingerprint_copied_to_clipboard);
|
|
||||||
assertThat("clipboard data is fingerprint", ClipboardReflection.getClipboardText(mActivity),
|
|
||||||
is("c619d53f7a5f96f391a84ca79d604d2f310716a3"));
|
|
||||||
|
|
||||||
intending(allOf(
|
|
||||||
hasAction("android.intent.action.CHOOSER"),
|
|
||||||
hasExtra(equalTo(Intent.EXTRA_INTENT), allOf(
|
|
||||||
hasAction(Intent.ACTION_SEND),
|
|
||||||
hasType("text/plain"),
|
|
||||||
hasExtra(is(Intent.EXTRA_TEXT), is("openpgp4fpr:c619d53f7a5f96f391a84ca79d604d2f310716a3")),
|
|
||||||
hasExtra(is(Intent.EXTRA_STREAM),
|
|
||||||
allOf(hasScheme("content"), hasHost(TemporaryFileProvider.AUTHORITY)))
|
|
||||||
))
|
|
||||||
)).respondWith(new ActivityResult(Activity.RESULT_OK, null));
|
|
||||||
onView(withId(R.id.view_key_action_fingerprint_share)).perform(click());
|
|
||||||
|
|
||||||
onView(withId(R.id.view_key_action_key_clipboard)).perform(click());
|
|
||||||
checkAndDismissSnackbar(Style.OK, R.string.key_copied_to_clipboard);
|
|
||||||
assertThat("clipboard data is key",
|
|
||||||
ClipboardReflection.getClipboardText(mActivity), startsWith("----"));
|
|
||||||
|
|
||||||
intending(allOf(
|
|
||||||
hasAction("android.intent.action.CHOOSER"),
|
|
||||||
hasExtra(equalTo(Intent.EXTRA_INTENT), allOf(
|
|
||||||
hasAction(Intent.ACTION_SEND),
|
|
||||||
hasType("text/plain"),
|
|
||||||
hasExtra(is(Intent.EXTRA_TEXT), startsWith("----")),
|
|
||||||
hasExtra(is(Intent.EXTRA_STREAM),
|
|
||||||
allOf(hasScheme("content"), hasHost(TemporaryFileProvider.AUTHORITY)))
|
|
||||||
))
|
|
||||||
)).respondWith(new ActivityResult(Activity.RESULT_OK, null));
|
|
||||||
onView(withId(R.id.view_key_action_key_share)).perform(click());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -19,7 +19,6 @@ package org.sufficientlysecure.keychain.operations;
|
|||||||
|
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.net.Uri;
|
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.operations.results.EditKeyResult;
|
import org.sufficientlysecure.keychain.operations.results.EditKeyResult;
|
||||||
@@ -29,7 +28,6 @@ import org.sufficientlysecure.keychain.pgp.Progressable;
|
|||||||
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
|
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
|
||||||
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
|
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
|
||||||
import org.sufficientlysecure.keychain.provider.KeyWritableRepository;
|
import org.sufficientlysecure.keychain.provider.KeyWritableRepository;
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
|
||||||
import org.sufficientlysecure.keychain.service.RevokeKeyringParcel;
|
import org.sufficientlysecure.keychain.service.RevokeKeyringParcel;
|
||||||
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
|
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
|
||||||
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
||||||
@@ -59,8 +57,7 @@ public class RevokeOperation extends BaseReadWriteOperation<RevokeKeyringParcel>
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
Uri secretUri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(masterKeyId);
|
CachedPublicKeyRing keyRing = mKeyRepository.getCachedPublicKeyRing(masterKeyId);
|
||||||
CachedPublicKeyRing keyRing = mKeyRepository.getCachedPublicKeyRing(secretUri);
|
|
||||||
|
|
||||||
// check if this is a master secret key we can work with
|
// check if this is a master secret key we can work with
|
||||||
switch (keyRing.getSecretKeyType(masterKeyId)) {
|
switch (keyRing.getSecretKeyType(masterKeyId)) {
|
||||||
|
|||||||
@@ -71,11 +71,9 @@ import org.sufficientlysecure.keychain.pgp.SecurityProblem.EncryptionAlgorithmPr
|
|||||||
import org.sufficientlysecure.keychain.pgp.SecurityProblem.KeySecurityProblem;
|
import org.sufficientlysecure.keychain.pgp.SecurityProblem.KeySecurityProblem;
|
||||||
import org.sufficientlysecure.keychain.pgp.SecurityProblem.MissingMdc;
|
import org.sufficientlysecure.keychain.pgp.SecurityProblem.MissingMdc;
|
||||||
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
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.CachedPublicKeyRing;
|
||||||
import org.sufficientlysecure.keychain.provider.KeyRepository;
|
import org.sufficientlysecure.keychain.provider.KeyRepository;
|
||||||
import org.sufficientlysecure.keychain.provider.KeyWritableRepository;
|
import org.sufficientlysecure.keychain.provider.KeyWritableRepository;
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
|
||||||
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
||||||
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
|
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
|
||||||
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.RequireAnyDecryptPassphraseBuilder;
|
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.RequireAnyDecryptPassphraseBuilder;
|
||||||
@@ -638,10 +636,12 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
|
|||||||
CachedPublicKeyRing cachedPublicKeyRing;
|
CachedPublicKeyRing cachedPublicKeyRing;
|
||||||
try {
|
try {
|
||||||
// get actual keyring object based on master key id
|
// get actual keyring object based on master key id
|
||||||
cachedPublicKeyRing = mKeyRepository.getCachedPublicKeyRing(
|
Long masterKeyId = mKeyRepository.getMasterKeyIdBySubkeyId(subKeyId);
|
||||||
KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(subKeyId)
|
if (masterKeyId == null) {
|
||||||
);
|
log.add(LogType.MSG_DC_ASKIP_NO_KEY, indent + 1);
|
||||||
long masterKeyId = cachedPublicKeyRing.getMasterKeyId();
|
continue;
|
||||||
|
}
|
||||||
|
cachedPublicKeyRing = mKeyRepository.getCachedPublicKeyRing(masterKeyId);
|
||||||
|
|
||||||
// allow only specific keys for decryption?
|
// allow only specific keys for decryption?
|
||||||
if (input.getAllowedKeyIds() != null) {
|
if (input.getAllowedKeyIds() != null) {
|
||||||
@@ -713,7 +713,7 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
|
|||||||
encryptedDataAsymmetric = encData;
|
encryptedDataAsymmetric = encData;
|
||||||
decryptionKey = candidateDecryptionKey;
|
decryptionKey = candidateDecryptionKey;
|
||||||
|
|
||||||
} catch (PgpKeyNotFoundException | KeyWritableRepository.NotFoundException e) {
|
} catch (KeyWritableRepository.NotFoundException e) {
|
||||||
// continue with the next packet in the while loop
|
// continue with the next packet in the while loop
|
||||||
log.add(LogType.MSG_DC_ASKIP_NO_KEY, indent + 1);
|
log.add(LogType.MSG_DC_ASKIP_NO_KEY, indent + 1);
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -651,8 +651,7 @@ public class PgpSignEncryptOperation extends BaseOperation<PgpSignEncryptInputPa
|
|||||||
private boolean processEncryptionMasterKeyId(int indent, OperationLog log, PgpSignEncryptData data,
|
private boolean processEncryptionMasterKeyId(int indent, OperationLog log, PgpSignEncryptData data,
|
||||||
PGPEncryptedDataGenerator cPk, long encryptMasterKeyId) {
|
PGPEncryptedDataGenerator cPk, long encryptMasterKeyId) {
|
||||||
try {
|
try {
|
||||||
CanonicalizedPublicKeyRing keyRing = mKeyRepository.getCanonicalizedPublicKeyRing(
|
CanonicalizedPublicKeyRing keyRing = mKeyRepository.getCanonicalizedPublicKeyRing(encryptMasterKeyId);
|
||||||
KeyRings.buildUnifiedKeyRingUri(encryptMasterKeyId));
|
|
||||||
Set<Long> encryptSubKeyIds = keyRing.getEncryptIds();
|
Set<Long> encryptSubKeyIds = keyRing.getEncryptIds();
|
||||||
for (Long subKeyId : encryptSubKeyIds) {
|
for (Long subKeyId : encryptSubKeyIds) {
|
||||||
CanonicalizedPublicKey key = keyRing.getPublicKey(subKeyId);
|
CanonicalizedPublicKey key = keyRing.getPublicKey(subKeyId);
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ import org.sufficientlysecure.keychain.pgp.SecurityProblem.InsecureSigningAlgori
|
|||||||
import org.sufficientlysecure.keychain.pgp.SecurityProblem.KeySecurityProblem;
|
import org.sufficientlysecure.keychain.pgp.SecurityProblem.KeySecurityProblem;
|
||||||
import org.sufficientlysecure.keychain.provider.KeyRepository;
|
import org.sufficientlysecure.keychain.provider.KeyRepository;
|
||||||
import org.sufficientlysecure.keychain.provider.KeyWritableRepository;
|
import org.sufficientlysecure.keychain.provider.KeyWritableRepository;
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
|
|
||||||
@@ -160,9 +159,11 @@ class PgpSignatureChecker {
|
|||||||
for (int i = 0; i < sigList.size(); ++i) {
|
for (int i = 0; i < sigList.size(); ++i) {
|
||||||
try {
|
try {
|
||||||
long sigKeyId = sigList.get(i).getKeyID();
|
long sigKeyId = sigList.get(i).getKeyID();
|
||||||
CanonicalizedPublicKeyRing signingRing = mKeyRepository.getCanonicalizedPublicKeyRing(
|
Long masterKeyId = mKeyRepository.getMasterKeyIdBySubkeyId(sigKeyId);
|
||||||
KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(sigKeyId)
|
if (masterKeyId == null) {
|
||||||
);
|
continue;
|
||||||
|
}
|
||||||
|
CanonicalizedPublicKeyRing signingRing = mKeyRepository.getCanonicalizedPublicKeyRing(masterKeyId);
|
||||||
CanonicalizedPublicKey keyCandidate = signingRing.getPublicKey(sigKeyId);
|
CanonicalizedPublicKey keyCandidate = signingRing.getPublicKey(sigKeyId);
|
||||||
if ( ! keyCandidate.canSign()) {
|
if ( ! keyCandidate.canSign()) {
|
||||||
continue;
|
continue;
|
||||||
@@ -183,9 +184,11 @@ class PgpSignatureChecker {
|
|||||||
for (int i = 0; i < sigList.size(); ++i) {
|
for (int i = 0; i < sigList.size(); ++i) {
|
||||||
try {
|
try {
|
||||||
long sigKeyId = sigList.get(i).getKeyID();
|
long sigKeyId = sigList.get(i).getKeyID();
|
||||||
CanonicalizedPublicKeyRing signingRing = mKeyRepository.getCanonicalizedPublicKeyRing(
|
Long masterKeyId = mKeyRepository.getMasterKeyIdBySubkeyId(sigKeyId);
|
||||||
KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(sigKeyId)
|
if (masterKeyId == null) {
|
||||||
);
|
continue;
|
||||||
|
}
|
||||||
|
CanonicalizedPublicKeyRing signingRing = mKeyRepository.getCanonicalizedPublicKeyRing(masterKeyId);
|
||||||
CanonicalizedPublicKey keyCandidate = signingRing.getPublicKey(sigKeyId);
|
CanonicalizedPublicKey keyCandidate = signingRing.getPublicKey(sigKeyId);
|
||||||
if ( ! keyCandidate.canSign()) {
|
if ( ! keyCandidate.canSign()) {
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ import android.net.Uri;
|
|||||||
import com.squareup.sqldelight.SqlDelightQuery;
|
import com.squareup.sqldelight.SqlDelightQuery;
|
||||||
import org.bouncycastle.bcpg.ArmoredOutputStream;
|
import org.bouncycastle.bcpg.ArmoredOutputStream;
|
||||||
import org.sufficientlysecure.keychain.model.Certification;
|
import org.sufficientlysecure.keychain.model.Certification;
|
||||||
import org.sufficientlysecure.keychain.model.CustomColumnAdapters;
|
|
||||||
import org.sufficientlysecure.keychain.model.KeyRingPublic;
|
import org.sufficientlysecure.keychain.model.KeyRingPublic;
|
||||||
import org.sufficientlysecure.keychain.model.SubKey;
|
import org.sufficientlysecure.keychain.model.SubKey;
|
||||||
import org.sufficientlysecure.keychain.model.SubKey.UnifiedKeyInfo;
|
import org.sufficientlysecure.keychain.model.SubKey.UnifiedKeyInfo;
|
||||||
@@ -175,11 +174,6 @@ public class KeyRepository extends AbstractDao {
|
|||||||
return getGenericData(KeyRings.buildUnifiedKeyRingUri(masterKeyId), proj, types);
|
return getGenericData(KeyRings.buildUnifiedKeyRingUri(masterKeyId), proj, types);
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getMasterKeyIdBySubKeyId(long subKeyId) throws NotFoundException {
|
|
||||||
return (Long) getGenericData(KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(subKeyId),
|
|
||||||
KeyRings.MASTER_KEY_ID, FIELD_TYPE_INTEGER);
|
|
||||||
}
|
|
||||||
|
|
||||||
public CachedPublicKeyRing getCachedPublicKeyRing(Uri queryUri) throws PgpKeyNotFoundException {
|
public CachedPublicKeyRing getCachedPublicKeyRing(Uri queryUri) throws PgpKeyNotFoundException {
|
||||||
long masterKeyId = new CachedPublicKeyRing(this, queryUri).extractOrGetMasterKeyId();
|
long masterKeyId = new CachedPublicKeyRing(this, queryUri).extractOrGetMasterKeyId();
|
||||||
return getCachedPublicKeyRing(masterKeyId);
|
return getCachedPublicKeyRing(masterKeyId);
|
||||||
@@ -189,59 +183,26 @@ public class KeyRepository extends AbstractDao {
|
|||||||
return new CachedPublicKeyRing(this, KeyRings.buildUnifiedKeyRingUri(id));
|
return new CachedPublicKeyRing(this, KeyRings.buildUnifiedKeyRingUri(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
public CanonicalizedPublicKeyRing getCanonicalizedPublicKeyRing(long id) throws NotFoundException {
|
public CanonicalizedPublicKeyRing getCanonicalizedPublicKeyRing(long masterKeyId) throws NotFoundException {
|
||||||
return getCanonicalizedPublicKeyRing(KeyRings.buildUnifiedKeyRingUri(id));
|
UnifiedKeyInfo unifiedKeyInfo = getUnifiedKeyInfo(masterKeyId);
|
||||||
}
|
if (unifiedKeyInfo == null) {
|
||||||
|
throw new NotFoundException();
|
||||||
public CanonicalizedPublicKeyRing getCanonicalizedPublicKeyRing(Uri queryUri) throws NotFoundException {
|
|
||||||
Cursor cursor = contentResolver.query(queryUri,
|
|
||||||
new String[] { KeyRings.MASTER_KEY_ID, KeyRings.VERIFIED }, null, null, null);
|
|
||||||
try {
|
|
||||||
if (cursor != null && cursor.moveToFirst()) {
|
|
||||||
long masterKeyId = cursor.getLong(0);
|
|
||||||
long verified = cursor.getLong(1);
|
|
||||||
|
|
||||||
byte[] publicKeyData = loadPublicKeyRingData(masterKeyId);
|
|
||||||
VerificationStatus verificationStatus = CustomColumnAdapters.VERIFICATON_STATUS_ADAPTER.decode(verified);
|
|
||||||
return new CanonicalizedPublicKeyRing(publicKeyData, verificationStatus);
|
|
||||||
} else {
|
|
||||||
throw new NotFoundException("Key not found!");
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
if (cursor != null) {
|
|
||||||
cursor.close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
byte[] publicKeyData = loadPublicKeyRingData(masterKeyId);
|
||||||
|
return new CanonicalizedPublicKeyRing(publicKeyData, unifiedKeyInfo.verified());
|
||||||
}
|
}
|
||||||
|
|
||||||
public CanonicalizedSecretKeyRing getCanonicalizedSecretKeyRing(long id) throws NotFoundException {
|
public CanonicalizedSecretKeyRing getCanonicalizedSecretKeyRing(long masterKeyId) throws NotFoundException {
|
||||||
return getCanonicalizedSecretKeyRing(KeyRings.buildUnifiedKeyRingUri(id));
|
UnifiedKeyInfo unifiedKeyInfo = getUnifiedKeyInfo(masterKeyId);
|
||||||
}
|
if (unifiedKeyInfo == null || !unifiedKeyInfo.has_any_secret()) {
|
||||||
|
throw new NotFoundException();
|
||||||
public CanonicalizedSecretKeyRing getCanonicalizedSecretKeyRing(Uri queryUri) throws NotFoundException {
|
|
||||||
Cursor cursor = contentResolver.query(queryUri,
|
|
||||||
new String[] { KeyRings.MASTER_KEY_ID, KeyRings.VERIFIED, KeyRings.HAS_ANY_SECRET }, null, null, null);
|
|
||||||
try {
|
|
||||||
if (cursor != null && cursor.moveToFirst()) {
|
|
||||||
long masterKeyId = cursor.getLong(0);
|
|
||||||
long verified = cursor.getLong(1);
|
|
||||||
int hasAnySecret = cursor.getInt(2);
|
|
||||||
if (hasAnySecret == 0) {
|
|
||||||
throw new NotFoundException("No secret key available or unknown public key!");
|
|
||||||
}
|
|
||||||
|
|
||||||
VerificationStatus verificationStatus = CustomColumnAdapters.VERIFICATON_STATUS_ADAPTER.decode(verified);
|
|
||||||
|
|
||||||
byte[] secretKeyData = loadSecretKeyRingData(masterKeyId);
|
|
||||||
return new CanonicalizedSecretKeyRing(secretKeyData, verificationStatus);
|
|
||||||
} else {
|
|
||||||
throw new NotFoundException("Key not found!");
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
if (cursor != null) {
|
|
||||||
cursor.close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
byte[] secretKeyData = loadSecretKeyRingData(masterKeyId);
|
||||||
|
if (secretKeyData == null) {
|
||||||
|
throw new IllegalStateException("Missing expected secret key data!");
|
||||||
|
}
|
||||||
|
return new CanonicalizedSecretKeyRing(secretKeyData, unifiedKeyInfo.verified());
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Long> getAllMasterKeyIds() {
|
public List<Long> getAllMasterKeyIds() {
|
||||||
@@ -259,6 +220,16 @@ public class KeyRepository extends AbstractDao {
|
|||||||
return mapAllRows(query, KeyRingPublic.FACTORY.selectAllMasterKeyIdsMapper()::map);
|
return mapAllRows(query, KeyRingPublic.FACTORY.selectAllMasterKeyIdsMapper()::map);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Long getMasterKeyIdBySubkeyId(long subKeyId) {
|
||||||
|
SqlDelightQuery query = SubKey.FACTORY.selectMasterKeyIdBySubkey(subKeyId);
|
||||||
|
try (Cursor cursor = getReadableDb().query(query)) {
|
||||||
|
if (cursor.moveToFirst()) {
|
||||||
|
return SubKey.FACTORY.selectMasterKeyIdBySubkeyMapper().map(cursor);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public UnifiedKeyInfo getUnifiedKeyInfo(long masterKeyId) {
|
public UnifiedKeyInfo getUnifiedKeyInfo(long masterKeyId) {
|
||||||
SqlDelightQuery query = SubKey.FACTORY.selectUnifiedKeyInfoByMasterKeyId(masterKeyId);
|
SqlDelightQuery query = SubKey.FACTORY.selectUnifiedKeyInfoByMasterKeyId(masterKeyId);
|
||||||
try (Cursor cursor = getReadableDb().query(query)) {
|
try (Cursor cursor = getReadableDb().query(query)) {
|
||||||
|
|||||||
@@ -627,10 +627,7 @@ public class OpenPgpService extends Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// try to find key, throws NotFoundException if not in db!
|
CanonicalizedPublicKeyRing keyRing = mKeyRepository.getCanonicalizedPublicKeyRing(masterKeyId);
|
||||||
CanonicalizedPublicKeyRing keyRing =
|
|
||||||
mKeyRepository.getCanonicalizedPublicKeyRing(
|
|
||||||
KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(masterKeyId));
|
|
||||||
|
|
||||||
Intent result = new Intent();
|
Intent result = new Intent();
|
||||||
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
|
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ import org.sufficientlysecure.keychain.provider.ApiAppDao;
|
|||||||
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
|
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
|
||||||
import org.sufficientlysecure.keychain.provider.KeyRepository;
|
import org.sufficientlysecure.keychain.provider.KeyRepository;
|
||||||
import org.sufficientlysecure.keychain.provider.KeyRepository.NotFoundException;
|
import org.sufficientlysecure.keychain.provider.KeyRepository.NotFoundException;
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
|
||||||
import org.sufficientlysecure.keychain.remote.ApiPermissionHelper;
|
import org.sufficientlysecure.keychain.remote.ApiPermissionHelper;
|
||||||
import org.sufficientlysecure.keychain.remote.ApiPermissionHelper.WrongPackageCertificateException;
|
import org.sufficientlysecure.keychain.remote.ApiPermissionHelper.WrongPackageCertificateException;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
@@ -118,9 +117,11 @@ class RequestKeyPermissionPresenter {
|
|||||||
CachedPublicKeyRing publicFallbackRing = null;
|
CachedPublicKeyRing publicFallbackRing = null;
|
||||||
for (long candidateSubKeyId : subKeyIds) {
|
for (long candidateSubKeyId : subKeyIds) {
|
||||||
try {
|
try {
|
||||||
CachedPublicKeyRing cachedPublicKeyRing = keyRepository.getCachedPublicKeyRing(
|
Long masterKeyId = keyRepository.getMasterKeyIdBySubkeyId(candidateSubKeyId);
|
||||||
KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(candidateSubKeyId)
|
if (masterKeyId == null) {
|
||||||
);
|
continue;
|
||||||
|
}
|
||||||
|
CachedPublicKeyRing cachedPublicKeyRing = keyRepository.getCachedPublicKeyRing(masterKeyId);
|
||||||
|
|
||||||
SecretKeyType secretKeyType = cachedPublicKeyRing.getSecretKeyType(candidateSubKeyId);
|
SecretKeyType secretKeyType = cachedPublicKeyRing.getSecretKeyType(candidateSubKeyId);
|
||||||
if (secretKeyType.isUsable()) {
|
if (secretKeyType.isUsable()) {
|
||||||
@@ -129,7 +130,7 @@ class RequestKeyPermissionPresenter {
|
|||||||
if (publicFallbackRing == null) {
|
if (publicFallbackRing == null) {
|
||||||
publicFallbackRing = cachedPublicKeyRing;
|
publicFallbackRing = cachedPublicKeyRing;
|
||||||
}
|
}
|
||||||
} catch (PgpKeyNotFoundException | NotFoundException e) {
|
} catch (NotFoundException e) {
|
||||||
// no matter
|
// no matter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ import android.support.v4.app.Fragment;
|
|||||||
import android.support.v4.app.LoaderManager;
|
import android.support.v4.app.LoaderManager;
|
||||||
import android.support.v4.content.CursorLoader;
|
import android.support.v4.content.CursorLoader;
|
||||||
import android.support.v4.content.Loader;
|
import android.support.v4.content.Loader;
|
||||||
import android.util.Log;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.View.OnClickListener;
|
import android.view.View.OnClickListener;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
@@ -41,14 +40,12 @@ import android.widget.ViewAnimator;
|
|||||||
import org.openintents.openpgp.OpenPgpDecryptionResult;
|
import org.openintents.openpgp.OpenPgpDecryptionResult;
|
||||||
import org.openintents.openpgp.OpenPgpSignatureResult;
|
import org.openintents.openpgp.OpenPgpSignatureResult;
|
||||||
import org.openintents.openpgp.util.OpenPgpUtils;
|
import org.openintents.openpgp.util.OpenPgpUtils;
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
|
||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
import org.sufficientlysecure.keychain.keyimport.HkpKeyserverAddress;
|
import org.sufficientlysecure.keychain.keyimport.HkpKeyserverAddress;
|
||||||
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
|
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
|
||||||
import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
|
import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
|
||||||
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
|
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
|
||||||
import org.sufficientlysecure.keychain.pgp.KeyRing;
|
import org.sufficientlysecure.keychain.pgp.KeyRing;
|
||||||
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
|
|
||||||
import org.sufficientlysecure.keychain.provider.KeyRepository;
|
import org.sufficientlysecure.keychain.provider.KeyRepository;
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
||||||
@@ -193,18 +190,14 @@ public abstract class DecryptFragment extends Fragment implements LoaderManager.
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void showKey(long keyId) {
|
private void showKey(long keyId) {
|
||||||
try {
|
KeyRepository keyRepository = KeyRepository.create(requireContext());
|
||||||
|
Long masterKeyId = keyRepository.getMasterKeyIdBySubkeyId(keyId);
|
||||||
KeyRepository keyRepository = KeyRepository.create(requireContext());
|
if (masterKeyId == null) {
|
||||||
long masterKeyId = keyRepository.getCachedPublicKeyRing(
|
|
||||||
KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(keyId)
|
|
||||||
).getMasterKeyId();
|
|
||||||
Intent viewKeyIntent = ViewKeyActivity.getViewKeyActivityIntent(requireActivity(), masterKeyId);
|
|
||||||
startActivity(viewKeyIntent);
|
|
||||||
|
|
||||||
} catch (PgpKeyNotFoundException e) {
|
|
||||||
Notify.create(getActivity(), R.string.error_key_not_found, Style.ERROR).show();
|
Notify.create(getActivity(), R.string.error_key_not_found, Style.ERROR).show();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
Intent viewKeyIntent = ViewKeyActivity.getViewKeyActivityIntent(requireActivity(), masterKeyId);
|
||||||
|
startActivity(viewKeyIntent);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void loadVerifyResult(DecryptVerifyResult decryptVerifyResult) {
|
protected void loadVerifyResult(DecryptVerifyResult decryptVerifyResult) {
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
|
|||||||
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
|
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
|
||||||
import org.sufficientlysecure.keychain.provider.KeyRepository;
|
import org.sufficientlysecure.keychain.provider.KeyRepository;
|
||||||
import org.sufficientlysecure.keychain.provider.KeyRepository.NotFoundException;
|
import org.sufficientlysecure.keychain.provider.KeyRepository.NotFoundException;
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
|
||||||
import org.sufficientlysecure.keychain.ui.adapter.KeyAdapter.KeyItem;
|
import org.sufficientlysecure.keychain.ui.adapter.KeyAdapter.KeyItem;
|
||||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||||
import org.sufficientlysecure.keychain.ui.util.Notify;
|
import org.sufficientlysecure.keychain.ui.util.Notify;
|
||||||
@@ -139,8 +138,7 @@ public class EncryptModeAsymmetricFragment extends EncryptModeFragment {
|
|||||||
private void preselectKeys(Long signatureKeyId, long[] encryptionKeyIds) {
|
private void preselectKeys(Long signatureKeyId, long[] encryptionKeyIds) {
|
||||||
if (signatureKeyId != null) {
|
if (signatureKeyId != null) {
|
||||||
try {
|
try {
|
||||||
CachedPublicKeyRing keyring = mKeyRepository.getCachedPublicKeyRing(
|
CachedPublicKeyRing keyring = mKeyRepository.getCachedPublicKeyRing(signatureKeyId);
|
||||||
KeyRings.buildUnifiedKeyRingUri(signatureKeyId));
|
|
||||||
if (keyring.hasAnySecret()) {
|
if (keyring.hasAnySecret()) {
|
||||||
mSignKeySpinner.setPreSelectedKeyId(signatureKeyId);
|
mSignKeySpinner.setPreSelectedKeyId(signatureKeyId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -285,10 +285,9 @@ public class PassphraseDialogActivity extends FragmentActivity {
|
|||||||
} else {
|
} else {
|
||||||
long subKeyId = subKeyIds[0];
|
long subKeyId = subKeyIds[0];
|
||||||
|
|
||||||
KeyRepository helper =
|
KeyRepository keyRepository = KeyRepository.create(getContext());
|
||||||
KeyRepository.create(getContext());
|
Long masterKeyId = keyRepository.getMasterKeyIdBySubkeyId(subKeyId);
|
||||||
CachedPublicKeyRing cachedPublicKeyRing = helper.getCachedPublicKeyRing(
|
CachedPublicKeyRing cachedPublicKeyRing = keyRepository.getCachedPublicKeyRing(masterKeyId);
|
||||||
KeychainContract.KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(subKeyId));
|
|
||||||
// yes the inner try/catch block is necessary, otherwise the final variable
|
// yes the inner try/catch block is necessary, otherwise the final variable
|
||||||
// above can't be statically verified to have been set in all cases because
|
// above can't be statically verified to have been set in all cases because
|
||||||
// the catch clause doesn't return.
|
// the catch clause doesn't return.
|
||||||
@@ -534,11 +533,13 @@ public class PassphraseDialogActivity extends FragmentActivity {
|
|||||||
|
|
||||||
CanonicalizedSecretKey canonicalizedSecretKey = null;
|
CanonicalizedSecretKey canonicalizedSecretKey = null;
|
||||||
for (long subKeyId : mRequiredInput.getSubKeyIds()) {
|
for (long subKeyId : mRequiredInput.getSubKeyIds()) {
|
||||||
CanonicalizedSecretKeyRing secretKeyRing =
|
KeyRepository keyRepository = KeyRepository.create(getContext());
|
||||||
KeyRepository.create(getContext()).getCanonicalizedSecretKeyRing(
|
Long masterKeyId = keyRepository.getMasterKeyIdBySubkeyId(subKeyId);
|
||||||
KeychainContract.KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(subKeyId));
|
if (masterKeyId == null) {
|
||||||
CanonicalizedSecretKey secretKeyToUnlock =
|
continue;
|
||||||
secretKeyRing.getSecretKey(subKeyId);
|
}
|
||||||
|
CanonicalizedSecretKeyRing secretKeyRing = keyRepository.getCanonicalizedSecretKeyRing(masterKeyId);
|
||||||
|
CanonicalizedSecretKey secretKeyToUnlock = secretKeyRing.getSecretKey(subKeyId);
|
||||||
|
|
||||||
// this is the operation may take a very long time (100ms to several seconds!)
|
// this is the operation may take a very long time (100ms to several seconds!)
|
||||||
boolean unlockSucceeded = secretKeyToUnlock.unlock(passphrase);
|
boolean unlockSucceeded = secretKeyToUnlock.unlock(passphrase);
|
||||||
|
|||||||
@@ -199,12 +199,11 @@ public class SecurityTokenOperationActivity extends BaseSecurityTokenActivity {
|
|||||||
throw new IOException(getString(R.string.error_wrong_security_token));
|
throw new IOException(getString(R.string.error_wrong_security_token));
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyRepository keyRepository =
|
KeyRepository keyRepository = KeyRepository.create(this);
|
||||||
KeyRepository.create(this);
|
|
||||||
CanonicalizedPublicKeyRing publicKeyRing;
|
CanonicalizedPublicKeyRing publicKeyRing;
|
||||||
try {
|
try {
|
||||||
publicKeyRing = keyRepository.getCanonicalizedPublicKeyRing(
|
Long masterKeyId = keyRepository.getMasterKeyIdBySubkeyId(mRequiredInput.getMasterKeyId());
|
||||||
KeychainContract.KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(mRequiredInput.getMasterKeyId()));
|
publicKeyRing = keyRepository.getCanonicalizedPublicKeyRing(masterKeyId);
|
||||||
} catch (KeyRepository.NotFoundException e) {
|
} catch (KeyRepository.NotFoundException e) {
|
||||||
throw new IOException("Couldn't find subkey for key to token operation.");
|
throw new IOException("Couldn't find subkey for key to token operation.");
|
||||||
}
|
}
|
||||||
@@ -263,9 +262,8 @@ public class SecurityTokenOperationActivity extends BaseSecurityTokenActivity {
|
|||||||
KeyRepository.create(this);
|
KeyRepository.create(this);
|
||||||
CanonicalizedSecretKeyRing secretKeyRing;
|
CanonicalizedSecretKeyRing secretKeyRing;
|
||||||
try {
|
try {
|
||||||
secretKeyRing = keyRepository.getCanonicalizedSecretKeyRing(
|
Long masterKeyId = keyRepository.getMasterKeyIdBySubkeyId(mRequiredInput.getMasterKeyId());
|
||||||
KeychainContract.KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(mRequiredInput.getMasterKeyId())
|
secretKeyRing = keyRepository.getCanonicalizedSecretKeyRing(masterKeyId);
|
||||||
);
|
|
||||||
} catch (KeyRepository.NotFoundException e) {
|
} catch (KeyRepository.NotFoundException e) {
|
||||||
throw new IOException("Couldn't find subkey for key to token operation.");
|
throw new IOException("Couldn't find subkey for key to token operation.");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -115,11 +115,13 @@ public abstract class PublicKeyRetrievalLoader extends AsyncTaskLoader<KeyRetrie
|
|||||||
|
|
||||||
log.add(LogType.MSG_RET_LOCAL_SEARCH, 1, KeyFormattingUtils.convertKeyIdToHex(keyId));
|
log.add(LogType.MSG_RET_LOCAL_SEARCH, 1, KeyFormattingUtils.convertKeyIdToHex(keyId));
|
||||||
try {
|
try {
|
||||||
CachedPublicKeyRing cachedPublicKeyRing = keyRepository.getCachedPublicKeyRing(
|
Long masterKeyId = keyRepository.getMasterKeyIdBySubkeyId(keyId);
|
||||||
KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(keyId)
|
if (masterKeyId == null) {
|
||||||
);
|
log.add(LogType.MSG_RET_LOCAL_NOT_FOUND, 2);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
CachedPublicKeyRing cachedPublicKeyRing = keyRepository.getCachedPublicKeyRing(masterKeyId);
|
||||||
|
|
||||||
long masterKeyId = cachedPublicKeyRing.getMasterKeyId();
|
|
||||||
// TODO check fingerprint
|
// TODO check fingerprint
|
||||||
// if (!Arrays.equals(fingerprints, cachedPublicKeyRing.getFingerprint())) {
|
// if (!Arrays.equals(fingerprints, cachedPublicKeyRing.getFingerprint())) {
|
||||||
// log.add(LogType.MSG_RET_LOCAL_FP_MISMATCH, 1);
|
// log.add(LogType.MSG_RET_LOCAL_FP_MISMATCH, 1);
|
||||||
@@ -147,7 +149,7 @@ public abstract class PublicKeyRetrievalLoader extends AsyncTaskLoader<KeyRetrie
|
|||||||
throw new IllegalStateException("Unhandled SecretKeyType!");
|
throw new IllegalStateException("Unhandled SecretKeyType!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (PgpKeyNotFoundException | NotFoundException e) {
|
} catch (NotFoundException e) {
|
||||||
log.add(LogType.MSG_RET_LOCAL_NOT_FOUND, 2);
|
log.add(LogType.MSG_RET_LOCAL_NOT_FOUND, 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,6 +57,11 @@ SELECT * FROM unifiedKeyView
|
|||||||
WHERE has_any_secret_int = 1
|
WHERE has_any_secret_int = 1
|
||||||
ORDER BY creation DESC;
|
ORDER BY creation DESC;
|
||||||
|
|
||||||
|
selectMasterKeyIdBySubkey:
|
||||||
|
SELECT master_key_id
|
||||||
|
FROM keys
|
||||||
|
WHERE key_id = ?;
|
||||||
|
|
||||||
selectSubkeysByMasterKeyId:
|
selectSubkeysByMasterKeyId:
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM keys
|
FROM keys
|
||||||
|
|||||||
@@ -266,14 +266,12 @@ public class InteropTest {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public CanonicalizedPublicKeyRing getCanonicalizedPublicKeyRing(Uri q)
|
public CanonicalizedPublicKeyRing getCanonicalizedPublicKeyRing(Uri q)
|
||||||
throws NotFoundException {
|
throws NotFoundException {
|
||||||
Assert.assertEquals(msg + ": query should be for verification key", q, verifyUri);
|
Assert.assertEquals(msg + ": query should be for verification key", q, verifyUri);
|
||||||
return verify;
|
return verify;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public CanonicalizedSecretKeyRing getCanonicalizedSecretKeyRing(Uri q)
|
public CanonicalizedSecretKeyRing getCanonicalizedSecretKeyRing(Uri q)
|
||||||
throws NotFoundException {
|
throws NotFoundException {
|
||||||
Assert.assertEquals(msg + ": query should be for the decryption key", q, decryptUri);
|
Assert.assertEquals(msg + ": query should be for the decryption key", q, decryptUri);
|
||||||
|
|||||||
Reference in New Issue
Block a user