treat missing keys (from 404 errors) individually during key import

This commit is contained in:
Vincent Breitmoser
2017-05-20 21:34:52 +02:00
parent add9bf8973
commit 1331d3960c
6 changed files with 83 additions and 24 deletions

View File

@@ -47,6 +47,14 @@ public abstract class Keyserver {
} }
} }
public static class QueryNotFoundException extends QueryFailedException {
private static final long serialVersionUID = 2693768928624654513L;
public QueryNotFoundException(String message) {
super(message);
}
}
public static class QueryNeedsRepairException extends CloudSearchFailureException { public static class QueryNeedsRepairException extends CloudSearchFailureException {
private static final long serialVersionUID = 2693768928624654512L; private static final long serialVersionUID = 2693768928624654512L;
} }

View File

@@ -402,6 +402,9 @@ public class ParcelableHkpKeyserver extends Keyserver implements Parcelable {
throw new Keyserver.QueryFailedException("Unsupported keyserver URI"); throw new Keyserver.QueryFailedException("Unsupported keyserver URI");
} catch (HttpError httpError) { } catch (HttpError httpError) {
Log.d(Constants.TAG, "Failed to get key at HkpKeyserver", httpError); Log.d(Constants.TAG, "Failed to get key at HkpKeyserver", httpError);
if (httpError.getCode() == 404) {
throw new Keyserver.QueryNotFoundException("not found");
}
throw new Keyserver.QueryFailedException("not found"); throw new Keyserver.QueryFailedException("not found");
} }
if (data == null) { if (data == null) {

View File

@@ -40,6 +40,7 @@ import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.keyimport.FacebookKeyserver; import org.sufficientlysecure.keychain.keyimport.FacebookKeyserver;
import org.sufficientlysecure.keychain.keyimport.KeybaseKeyserver; import org.sufficientlysecure.keychain.keyimport.KeybaseKeyserver;
import org.sufficientlysecure.keychain.keyimport.Keyserver; import org.sufficientlysecure.keychain.keyimport.Keyserver;
import org.sufficientlysecure.keychain.keyimport.Keyserver.QueryNotFoundException;
import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver; import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing; import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.network.orbot.OrbotHelper; import org.sufficientlysecure.keychain.network.orbot.OrbotHelper;
@@ -152,7 +153,7 @@ public class ImportOperation extends BaseReadWriteOperation<ImportKeyringParcel>
return new ImportKeyResult(ImportKeyResult.RESULT_FAIL_NOTHING, log); return new ImportKeyResult(ImportKeyResult.RESULT_FAIL_NOTHING, log);
} }
int newKeys = 0, updatedKeys = 0, badKeys = 0, secret = 0; int newKeys = 0, updatedKeys = 0, missingKeys = 0, badKeys = 0, secret = 0;
ArrayList<Long> importedMasterKeyIds = new ArrayList<>(); ArrayList<Long> importedMasterKeyIds = new ArrayList<>();
ArrayList<CanonicalizedKeyRing> canKeyRings = new ArrayList<>(); ArrayList<CanonicalizedKeyRing> canKeyRings = new ArrayList<>();
@@ -170,6 +171,8 @@ public class ImportOperation extends BaseReadWriteOperation<ImportKeyringParcel>
break; break;
} }
boolean keyWasDownloaded = false;
try { try {
UncachedKeyRing key = null; UncachedKeyRing key = null;
@@ -178,13 +181,25 @@ public class ImportOperation extends BaseReadWriteOperation<ImportKeyringParcel>
if (entry.mBytes != null) { if (entry.mBytes != null) {
key = UncachedKeyRing.decodeFromData(entry.mBytes); key = UncachedKeyRing.decodeFromData(entry.mBytes);
} else { } else {
key = fetchKeyFromInternet(hkpKeyserver, proxy, log, entry, key); try {
key = fetchKeyFromInternet(hkpKeyserver, proxy, log, entry, key);
} catch (QueryNotFoundException e) {
// note that this does NOT fire on network errors! those will be logged inline and return in null
log.add(LogType.MSG_IMPORT_FETCH_ERROR_NOT_FOUND, 2);
missingKeys += 1;
if (key.isSecret()) {
log.add(LogType.MSG_IMPORT_FETCH_ERROR_KEYSERVER_SECRET, 2);
badKeys += 1;
continue; continue;
} }
if (key != null) {
keyWasDownloaded = true;
if (key.isSecret()) {
log.add(LogType.MSG_IMPORT_FETCH_ERROR_KEYSERVER_SECRET, 2);
badKeys += 1;
continue;
}
}
} }
if (key == null) { if (key == null) {
@@ -266,6 +281,7 @@ public class ImportOperation extends BaseReadWriteOperation<ImportKeyringParcel>
// special return case: no new keys at all // special return case: no new keys at all
if (badKeys == 0 && newKeys == 0 && updatedKeys == 0) { if (badKeys == 0 && newKeys == 0 && updatedKeys == 0) {
// if keys merely aren't on keyservers, it's just a warning
resultType = ImportKeyResult.RESULT_FAIL_NOTHING; resultType = ImportKeyResult.RESULT_FAIL_NOTHING;
} else { } else {
if (newKeys > 0) { if (newKeys > 0) {
@@ -296,19 +312,28 @@ public class ImportOperation extends BaseReadWriteOperation<ImportKeyringParcel>
} }
} }
ImportKeyResult result = new ImportKeyResult(resultType, log, newKeys, updatedKeys, badKeys, secret, ImportKeyResult result = new ImportKeyResult(
importedMasterKeyIdsArray); resultType, log, newKeys, updatedKeys, missingKeys, badKeys, secret, importedMasterKeyIdsArray);
result.setCanonicalizedKeyRings(canKeyRings); result.setCanonicalizedKeyRings(canKeyRings);
return result; return result;
} }
private UncachedKeyRing fetchKeyFromInternet(ParcelableHkpKeyserver hkpKeyserver, @NonNull ParcelableProxy proxy, private UncachedKeyRing fetchKeyFromInternet(ParcelableHkpKeyserver hkpKeyserver, @NonNull ParcelableProxy proxy,
OperationLog log, ParcelableKeyRing entry, UncachedKeyRing key) throws PgpGeneralException, IOException { OperationLog log, ParcelableKeyRing entry, UncachedKeyRing key)
throws PgpGeneralException, IOException, QueryNotFoundException {
QueryNotFoundException queryNotFoundException = null;
boolean canFetchFromKeyservers = boolean canFetchFromKeyservers =
hkpKeyserver != null && (entry.mKeyIdHex != null || entry.mExpectedFingerprint != null); hkpKeyserver != null && (entry.mKeyIdHex != null || entry.mExpectedFingerprint != null);
if (canFetchFromKeyservers) { if (canFetchFromKeyservers) {
UncachedKeyRing keyserverKey = fetchKeyFromKeyserver(hkpKeyserver, proxy, log, entry); UncachedKeyRing keyserverKey = null;
try {
keyserverKey = fetchKeyFromKeyserver(hkpKeyserver, proxy, log, entry);
} catch (QueryNotFoundException e) {
queryNotFoundException = e;
}
if (keyserverKey != null) { if (keyserverKey != null) {
key = keyserverKey; key = keyserverKey;
} }
@@ -329,12 +354,17 @@ public class ImportOperation extends BaseReadWriteOperation<ImportKeyringParcel>
key = mergeKeysOrUseEither(log, 3, key, facebookKey); key = mergeKeysOrUseEither(log, 3, key, facebookKey);
} }
} }
if (key == null && queryNotFoundException != null) {
throw queryNotFoundException;
}
return key; return key;
} }
@Nullable @Nullable
private UncachedKeyRing fetchKeyFromKeyserver(ParcelableHkpKeyserver hkpKeyserver, @NonNull ParcelableProxy proxy, private UncachedKeyRing fetchKeyFromKeyserver(ParcelableHkpKeyserver hkpKeyserver, @NonNull ParcelableProxy proxy,
OperationLog log, ParcelableKeyRing entry) throws PgpGeneralException, IOException { OperationLog log, ParcelableKeyRing entry) throws PgpGeneralException, IOException, Keyserver.QueryNotFoundException {
try { try {
byte[] data; byte[] data;
log.add(LogType.MSG_IMPORT_KEYSERVER, 1, hkpKeyserver); log.add(LogType.MSG_IMPORT_KEYSERVER, 1, hkpKeyserver);
@@ -357,6 +387,8 @@ public class ImportOperation extends BaseReadWriteOperation<ImportKeyringParcel>
} }
return keyserverKey; return keyserverKey;
} catch (Keyserver.QueryNotFoundException e) {
throw e;
} catch (Keyserver.QueryFailedException e) { } catch (Keyserver.QueryFailedException e) {
Log.d(Constants.TAG, "query failed", e); Log.d(Constants.TAG, "query failed", e);
log.add(LogType.MSG_IMPORT_FETCH_ERROR_KEYSERVER, 3, e.getMessage()); log.add(LogType.MSG_IMPORT_FETCH_ERROR_KEYSERVER, 3, e.getMessage());
@@ -473,7 +505,6 @@ public class ImportOperation extends BaseReadWriteOperation<ImportKeyringParcel>
private ImportKeyResult multiThreadedKeyImport(ArrayList<ParcelableKeyRing> keyList, private ImportKeyResult multiThreadedKeyImport(ArrayList<ParcelableKeyRing> keyList,
final ParcelableHkpKeyserver keyServer, final ParcelableProxy proxy, final ParcelableHkpKeyserver keyServer, final ParcelableProxy proxy,
final boolean skipSave) { final boolean skipSave) {
Log.d(Constants.TAG, "Multi-threaded key import starting"); Log.d(Constants.TAG, "Multi-threaded key import starting");
final Iterator<ParcelableKeyRing> keyListIterator = keyList.iterator(); final Iterator<ParcelableKeyRing> keyListIterator = keyList.iterator();
@@ -540,6 +571,7 @@ public class ImportOperation extends BaseReadWriteOperation<ImportKeyringParcel>
private int mBadKeys = 0; private int mBadKeys = 0;
private int mNewKeys = 0; private int mNewKeys = 0;
private int mUpdatedKeys = 0; private int mUpdatedKeys = 0;
private int mMissingKeys = 0;
private int mSecret = 0; private int mSecret = 0;
private int mResultType = 0; private int mResultType = 0;
private boolean mHasCancelledResult; private boolean mHasCancelledResult;
@@ -586,6 +618,7 @@ public class ImportOperation extends BaseReadWriteOperation<ImportKeyringParcel>
mBadKeys += result.mBadKeys; mBadKeys += result.mBadKeys;
mNewKeys += result.mNewKeys; mNewKeys += result.mNewKeys;
mUpdatedKeys += result.mUpdatedKeys; mUpdatedKeys += result.mUpdatedKeys;
mMissingKeys += result.mMissingKeys;
mSecret += result.mSecret; mSecret += result.mSecret;
long[] masterKeyIds = result.getImportedMasterKeyIds(); long[] masterKeyIds = result.getImportedMasterKeyIds();
@@ -635,7 +668,7 @@ public class ImportOperation extends BaseReadWriteOperation<ImportKeyringParcel>
} }
ImportKeyResult result = new ImportKeyResult(mResultType, mImportLog, mNewKeys, ImportKeyResult result = new ImportKeyResult(mResultType, mImportLog, mNewKeys,
mUpdatedKeys, mBadKeys, mSecret, masterKeyIds); mUpdatedKeys, mMissingKeys, mBadKeys, mSecret, masterKeyIds);
result.setCanonicalizedKeyRings(mCanonicalizedKeyRings); result.setCanonicalizedKeyRings(mCanonicalizedKeyRings);
return result; return result;

View File

@@ -18,6 +18,9 @@
package org.sufficientlysecure.keychain.operations.results; package org.sufficientlysecure.keychain.operations.results;
import java.util.ArrayList;
import android.app.Activity; import android.app.Activity;
import android.content.Intent; import android.content.Intent;
import android.os.Parcel; import android.os.Parcel;
@@ -33,11 +36,9 @@ import org.sufficientlysecure.keychain.ui.util.Notify.ActionListener;
import org.sufficientlysecure.keychain.ui.util.Notify.Showable; import org.sufficientlysecure.keychain.ui.util.Notify.Showable;
import org.sufficientlysecure.keychain.ui.util.Notify.Style; import org.sufficientlysecure.keychain.ui.util.Notify.Style;
import java.util.ArrayList;
public class ImportKeyResult extends InputPendingResult { public class ImportKeyResult extends InputPendingResult {
public final int mNewKeys, mUpdatedKeys, mBadKeys, mSecret; public final int mNewKeys, mUpdatedKeys, mMissingKeys, mBadKeys, mSecret;
public final long[] mImportedMasterKeyIds; public final long[] mImportedMasterKeyIds;
// NOT PARCELED // NOT PARCELED
@@ -74,6 +75,10 @@ public class ImportKeyResult extends InputPendingResult {
return (mResult & RESULT_FAIL_NOTHING) == RESULT_FAIL_NOTHING; return (mResult & RESULT_FAIL_NOTHING) == RESULT_FAIL_NOTHING;
} }
public boolean isFailMissing() {
return isFailNothing() && mMissingKeys > 0;
}
public long[] getImportedMasterKeyIds() { public long[] getImportedMasterKeyIds() {
return mImportedMasterKeyIds; return mImportedMasterKeyIds;
} }
@@ -82,21 +87,23 @@ public class ImportKeyResult extends InputPendingResult {
super(source); super(source);
mNewKeys = source.readInt(); mNewKeys = source.readInt();
mUpdatedKeys = source.readInt(); mUpdatedKeys = source.readInt();
mMissingKeys = source.readInt();
mBadKeys = source.readInt(); mBadKeys = source.readInt();
mSecret = source.readInt(); mSecret = source.readInt();
mImportedMasterKeyIds = source.createLongArray(); mImportedMasterKeyIds = source.createLongArray();
} }
public ImportKeyResult(int result, OperationLog log) { public ImportKeyResult(int result, OperationLog log) {
this(result, log, 0, 0, 0, 0, new long[]{}); this(result, log, 0, 0, 0, 0, 0, new long[]{});
} }
public ImportKeyResult(int result, OperationLog log, public ImportKeyResult(int result, OperationLog log,
int newKeys, int updatedKeys, int badKeys, int secret, int newKeys, int updatedKeys, int missingKeys, int badKeys, int secret,
long[] importedMasterKeyIds) { long[] importedMasterKeyIds) {
super(result, log); super(result, log);
mNewKeys = newKeys; mNewKeys = newKeys;
mUpdatedKeys = updatedKeys; mUpdatedKeys = updatedKeys;
mMissingKeys = missingKeys;
mBadKeys = badKeys; mBadKeys = badKeys;
mSecret = secret; mSecret = secret;
mImportedMasterKeyIds = importedMasterKeyIds; mImportedMasterKeyIds = importedMasterKeyIds;
@@ -108,6 +115,7 @@ public class ImportKeyResult extends InputPendingResult {
// just assign default values, we won't use them anyway // just assign default values, we won't use them anyway
mNewKeys = 0; mNewKeys = 0;
mUpdatedKeys = 0; mUpdatedKeys = 0;
mMissingKeys = 0;
mBadKeys = 0; mBadKeys = 0;
mSecret = 0; mSecret = 0;
mImportedMasterKeyIds = new long[]{}; mImportedMasterKeyIds = new long[]{};
@@ -122,6 +130,7 @@ public class ImportKeyResult extends InputPendingResult {
super.writeToParcel(dest, flags); super.writeToParcel(dest, flags);
dest.writeInt(mNewKeys); dest.writeInt(mNewKeys);
dest.writeInt(mUpdatedKeys); dest.writeInt(mUpdatedKeys);
dest.writeInt(mMissingKeys);
dest.writeInt(mBadKeys); dest.writeInt(mBadKeys);
dest.writeInt(mSecret); dest.writeInt(mSecret);
dest.writeLongArray(mImportedMasterKeyIds); dest.writeLongArray(mImportedMasterKeyIds);
@@ -190,17 +199,20 @@ public class ImportKeyResult extends InputPendingResult {
} }
} else { } else {
duration = 0; if (isFailMissing()) {
style = Style.ERROR; duration = 0;
if (isFailNothing()) { style = Style.WARN;
str = activity.getResources().getString(R.string.import_warn_missing);
} else if (isFailNothing()) {
duration = 0;
style = Style.ERROR;
str = activity.getString((resultType & ImportKeyResult.RESULT_CANCELLED) > 0 str = activity.getString((resultType & ImportKeyResult.RESULT_CANCELLED) > 0
? R.string.import_error_nothing_cancelled ? R.string.import_error_nothing_cancelled
: R.string.import_error_nothing); : R.string.import_error_nothing);
} else { } else {
str = activity.getResources().getQuantityString( duration = 0;
R.plurals.import_error, style = Style.ERROR;
mBadKeys, str = activity.getResources().getQuantityString(R.plurals.import_error, mBadKeys, mBadKeys);
mBadKeys);
} }
} }

View File

@@ -769,6 +769,7 @@ public abstract class OperationResult implements Parcelable {
MSG_IMPORT_FETCH_ERROR (LogLevel.ERROR, R.string.msg_import_fetch_error), MSG_IMPORT_FETCH_ERROR (LogLevel.ERROR, R.string.msg_import_fetch_error),
MSG_IMPORT_FETCH_ERROR_DECODE (LogLevel.ERROR, R.string.msg_import_fetch_error_decode), MSG_IMPORT_FETCH_ERROR_DECODE (LogLevel.ERROR, R.string.msg_import_fetch_error_decode),
MSG_IMPORT_FETCH_ERROR_NOT_FOUND (LogLevel.ERROR, R.string.msg_import_fetch_error_not_found),
MSG_IMPORT_FETCH_ERROR_KEYSERVER(LogLevel.ERROR, R.string.msg_import_fetch_error_keyserver), MSG_IMPORT_FETCH_ERROR_KEYSERVER(LogLevel.ERROR, R.string.msg_import_fetch_error_keyserver),
MSG_IMPORT_FETCH_ERROR_KEYSERVER_SECRET (LogLevel.ERROR, R.string.msg_import_fetch_error_keyserver_secret), MSG_IMPORT_FETCH_ERROR_KEYSERVER_SECRET (LogLevel.ERROR, R.string.msg_import_fetch_error_keyserver_secret),
MSG_IMPORT_FETCH_KEYBASE (LogLevel.INFO, R.string.msg_import_fetch_keybase), MSG_IMPORT_FETCH_KEYBASE (LogLevel.INFO, R.string.msg_import_fetch_keybase),

View File

@@ -585,6 +585,7 @@
<item quantity="one">"Import failed!"</item> <item quantity="one">"Import failed!"</item>
<item quantity="other">"Import of %d keys failed!"</item> <item quantity="other">"Import of %d keys failed!"</item>
</plurals> </plurals>
<string name="import_warn_missing">"Key not found on keyservers."</string>
<string name="import_error_nothing">"Nothing to import."</string> <string name="import_error_nothing">"Nothing to import."</string>
<string name="import_error_nothing_cancelled">"Import cancelled."</string> <string name="import_error_nothing_cancelled">"Import cancelled."</string>
@@ -1337,6 +1338,7 @@
</plurals> </plurals>
<string name="msg_import_fetch_error_decode">"Error decoding retrieved keyring!"</string> <string name="msg_import_fetch_error_decode">"Error decoding retrieved keyring!"</string>
<string name="msg_import_fetch_error">"Key could not be retrieved! (Network problems?)"</string> <string name="msg_import_fetch_error">"Key could not be retrieved! (Network problems?)"</string>
<string name="msg_import_fetch_error_not_found">"Key not found!"</string>
<string name="msg_import_fetch_error_keyserver">"Could not retrieve key from keyservers: %s"</string> <string name="msg_import_fetch_error_keyserver">"Could not retrieve key from keyservers: %s"</string>
<string name="msg_import_fetch_error_keyserver_secret">"Cannot import secret key from keyserver!"</string> <string name="msg_import_fetch_error_keyserver_secret">"Cannot import secret key from keyserver!"</string>
<string name="msg_import_fetch_keybase">"Retrieving from keybase.io: %s"</string> <string name="msg_import_fetch_keybase">"Retrieving from keybase.io: %s"</string>