Merge branch 'master' of github.com:open-keychain/open-keychain

This commit is contained in:
Dominik Schürmann
2015-07-08 03:12:24 +02:00
28 changed files with 363 additions and 226 deletions

View File

@@ -29,6 +29,7 @@ import org.sufficientlysecure.keychain.operations.results.SaveKeyringResult;
import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing; import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey; import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing; import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing;
import org.sufficientlysecure.keychain.pgp.PassphraseCacheInterface;
import org.sufficientlysecure.keychain.pgp.PgpCertifyOperation; import org.sufficientlysecure.keychain.pgp.PgpCertifyOperation;
import org.sufficientlysecure.keychain.pgp.PgpCertifyOperation.PgpCertifyResult; import org.sufficientlysecure.keychain.pgp.PgpCertifyOperation.PgpCertifyResult;
import org.sufficientlysecure.keychain.pgp.Progressable; import org.sufficientlysecure.keychain.pgp.Progressable;
@@ -90,14 +91,21 @@ public class CertifyOperation extends BaseOperation<CertifyActionsParcel> {
case PIN: case PIN:
case PATTERN: case PATTERN:
case PASSPHRASE: case PASSPHRASE:
if (!cryptoInput.hasPassphrase()) { passphrase = cryptoInput.getPassphrase();
if (passphrase == null) {
try {
passphrase = getCachedPassphrase(certificationKey.getKeyId(), certificationKey.getKeyId());
} catch (PassphraseCacheInterface.NoSecretKeyException ignored) {
// treat as a cache miss for error handling purposes
}
}
if (passphrase == null) {
return new CertifyResult(log, return new CertifyResult(log,
RequiredInputParcel.createRequiredSignPassphrase( RequiredInputParcel.createRequiredSignPassphrase(
certificationKey.getKeyId(), certificationKey.getKeyId(), null) certificationKey.getKeyId(), certificationKey.getKeyId(), null)
); );
} }
// certification is always with the master key id, so use that one
passphrase = cryptoInput.getPassphrase();
break; break;
case PASSPHRASE_EMPTY: case PASSPHRASE_EMPTY:
@@ -105,6 +113,7 @@ public class CertifyOperation extends BaseOperation<CertifyActionsParcel> {
break; break;
case DIVERT_TO_CARD: case DIVERT_TO_CARD:
// the unlock operation will succeed for passphrase == null in a divertToCard key
passphrase = null; passphrase = null;
break; break;

View File

@@ -482,6 +482,7 @@ public abstract class OperationResult implements Parcelable {
// secret key modify // secret key modify
MSG_MF (LogLevel.START, R.string.msg_mr), MSG_MF (LogLevel.START, R.string.msg_mr),
MSG_MF_DIVERT (LogLevel.DEBUG, R.string.msg_mf_divert), MSG_MF_DIVERT (LogLevel.DEBUG, R.string.msg_mf_divert),
MSG_MF_ERROR_DIVERT_NEWSUB (LogLevel.ERROR, R.string.msg_mf_error_divert_newsub),
MSG_MF_ERROR_DIVERT_SERIAL (LogLevel.ERROR, R.string.msg_mf_error_divert_serial), MSG_MF_ERROR_DIVERT_SERIAL (LogLevel.ERROR, R.string.msg_mf_error_divert_serial),
MSG_MF_ERROR_ENCODE (LogLevel.ERROR, R.string.msg_mf_error_encode), MSG_MF_ERROR_ENCODE (LogLevel.ERROR, R.string.msg_mf_error_encode),
MSG_MF_ERROR_FINGERPRINT (LogLevel.ERROR, R.string.msg_mf_error_fingerprint), MSG_MF_ERROR_FINGERPRINT (LogLevel.ERROR, R.string.msg_mf_error_fingerprint),
@@ -499,6 +500,7 @@ public abstract class OperationResult implements Parcelable {
MSG_MF_ERROR_RESTRICTED(LogLevel.ERROR, R.string.msg_mf_error_restricted), MSG_MF_ERROR_RESTRICTED(LogLevel.ERROR, R.string.msg_mf_error_restricted),
MSG_MF_ERROR_REVOKED_PRIMARY (LogLevel.ERROR, R.string.msg_mf_error_revoked_primary), MSG_MF_ERROR_REVOKED_PRIMARY (LogLevel.ERROR, R.string.msg_mf_error_revoked_primary),
MSG_MF_ERROR_SIG (LogLevel.ERROR, R.string.msg_mf_error_sig), MSG_MF_ERROR_SIG (LogLevel.ERROR, R.string.msg_mf_error_sig),
MSG_MF_ERROR_SUB_STRIPPED(LogLevel.ERROR, R.string.msg_mf_error_sub_stripped),
MSG_MF_ERROR_SUBKEY_MISSING(LogLevel.ERROR, R.string.msg_mf_error_subkey_missing), MSG_MF_ERROR_SUBKEY_MISSING(LogLevel.ERROR, R.string.msg_mf_error_subkey_missing),
MSG_MF_ERROR_CONFLICTING_NFC_COMMANDS(LogLevel.ERROR, R.string.msg_mf_error_conflicting_nfc_commands), MSG_MF_ERROR_CONFLICTING_NFC_COMMANDS(LogLevel.ERROR, R.string.msg_mf_error_conflicting_nfc_commands),
MSG_MF_ERROR_DUPLICATE_KEYTOCARD_FOR_SLOT(LogLevel.ERROR, R.string.msg_mf_error_duplicate_keytocard_for_slot), MSG_MF_ERROR_DUPLICATE_KEYTOCARD_FOR_SLOT(LogLevel.ERROR, R.string.msg_mf_error_duplicate_keytocard_for_slot),

View File

@@ -897,18 +897,35 @@ public class PgpKeyOperation {
pKey = PGPPublicKey.removeCertification(pKey, sig); pKey = PGPPublicKey.removeCertification(pKey, sig);
} }
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder() PGPPrivateKey subPrivateKey;
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( if (!isDivertToCard(sKey)) {
cryptoInput.getPassphrase().getCharArray()); PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
PGPPrivateKey subPrivateKey = sKey.extractPrivateKey(keyDecryptor); .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
PGPSignature sig = generateSubkeyBindingSignature( cryptoInput.getPassphrase().getCharArray());
getSignatureGenerator(masterSecretKey, cryptoInput), subPrivateKey = sKey.extractPrivateKey(keyDecryptor);
cryptoInput.getSignatureTime(), // super special case: subkey is allowed to sign, but isn't available
masterPublicKey, masterPrivateKey, subPrivateKey, pKey, flags, expiry); if (subPrivateKey == null) {
log.add(LogType.MSG_MF_ERROR_SUB_STRIPPED,
indent + 1, KeyFormattingUtils.convertKeyIdToHex(change.mKeyId));
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
} else {
subPrivateKey = null;
}
try {
PGPSignature sig = generateSubkeyBindingSignature(
getSignatureGenerator(masterSecretKey, cryptoInput),
cryptoInput.getSignatureTime(), masterPublicKey, masterPrivateKey,
getSignatureGenerator(sKey, cryptoInput), subPrivateKey,
pKey, flags, expiry);
// generate and add new signature
pKey = PGPPublicKey.addCertification(pKey, sig);
sKR = PGPSecretKeyRing.insertSecretKey(sKR, PGPSecretKey.replacePublicKey(sKey, pKey));
} catch (NfcInteractionNeeded e) {
nfcSignOps.addHash(e.hashToSign, e.hashAlgo);
}
// generate and add new signature
pKey = PGPPublicKey.addCertification(pKey, sig);
sKR = PGPSecretKeyRing.insertSecretKey(sKR, PGPSecretKey.replacePublicKey(sKey, pKey));
} }
subProgressPop(); subProgressPop();
@@ -959,6 +976,11 @@ public class PgpKeyOperation {
log.add(LogType.MSG_MF_SUBKEY_NEW, indent, log.add(LogType.MSG_MF_SUBKEY_NEW, indent,
KeyFormattingUtils.getAlgorithmInfo(add.mAlgorithm, add.mKeySize, add.mCurve) ); KeyFormattingUtils.getAlgorithmInfo(add.mAlgorithm, add.mKeySize, add.mCurve) );
if (isDivertToCard(masterSecretKey)) {
log.add(LogType.MSG_MF_ERROR_DIVERT_NEWSUB, indent +1);
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
if (add.mExpiry == null) { if (add.mExpiry == null) {
log.add(LogType.MSG_MF_ERROR_NULL_EXPIRY, indent +1); log.add(LogType.MSG_MF_ERROR_NULL_EXPIRY, indent +1);
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null); return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
@@ -987,7 +1009,8 @@ public class PgpKeyOperation {
PGPSignature cert = generateSubkeyBindingSignature( PGPSignature cert = generateSubkeyBindingSignature(
getSignatureGenerator(masterSecretKey, cryptoInput), getSignatureGenerator(masterSecretKey, cryptoInput),
cryptoInput.getSignatureTime(), cryptoInput.getSignatureTime(),
masterPublicKey, masterPrivateKey, keyPair.getPrivateKey(), pKey, masterPublicKey, masterPrivateKey,
getSignatureGenerator(pKey, cryptoInput, false), keyPair.getPrivateKey(), pKey,
add.mFlags, add.mExpiry); add.mFlags, add.mExpiry);
pKey = PGPPublicKey.addSubkeyBindingCertification(pKey, cert); pKey = PGPPublicKey.addSubkeyBindingCertification(pKey, cert);
} catch (NfcInteractionNeeded e) { } catch (NfcInteractionNeeded e) {
@@ -1402,22 +1425,27 @@ public class PgpKeyOperation {
static PGPSignatureGenerator getSignatureGenerator( static PGPSignatureGenerator getSignatureGenerator(
PGPSecretKey secretKey, CryptoInputParcel cryptoInput) { PGPSecretKey secretKey, CryptoInputParcel cryptoInput) {
PGPContentSignerBuilder builder;
S2K s2k = secretKey.getS2K(); S2K s2k = secretKey.getS2K();
if (s2k != null && s2k.getType() == S2K.GNU_DUMMY_S2K boolean isDivertToCard = s2k != null && s2k.getType() == S2K.GNU_DUMMY_S2K
&& s2k.getProtectionMode() == S2K.GNU_PROTECTION_MODE_DIVERT_TO_CARD) { && s2k.getProtectionMode() == S2K.GNU_PROTECTION_MODE_DIVERT_TO_CARD;
return getSignatureGenerator(secretKey.getPublicKey(), cryptoInput, isDivertToCard);
}
static PGPSignatureGenerator getSignatureGenerator(
PGPPublicKey pKey, CryptoInputParcel cryptoInput, boolean divertToCard) {
PGPContentSignerBuilder builder;
if (divertToCard) {
// use synchronous "NFC based" SignerBuilder // use synchronous "NFC based" SignerBuilder
builder = new NfcSyncPGPContentSignerBuilder( builder = new NfcSyncPGPContentSignerBuilder(
secretKey.getPublicKey().getAlgorithm(), pKey.getAlgorithm(), PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO,
PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO, pKey.getKeyID(), cryptoInput.getCryptoData())
secretKey.getKeyID(), cryptoInput.getCryptoData())
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
} else { } else {
// content signer based on signing key algorithm and chosen hash algorithm // content signer based on signing key algorithm and chosen hash algorithm
builder = new JcaPGPContentSignerBuilder( builder = new JcaPGPContentSignerBuilder(
secretKey.getPublicKey().getAlgorithm(), pKey.getAlgorithm(), PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO)
PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
} }
@@ -1524,7 +1552,8 @@ public class PgpKeyOperation {
static PGPSignature generateSubkeyBindingSignature( static PGPSignature generateSubkeyBindingSignature(
PGPSignatureGenerator sGen, Date creationTime, PGPSignatureGenerator sGen, Date creationTime,
PGPPublicKey masterPublicKey, PGPPrivateKey masterPrivateKey, PGPPublicKey masterPublicKey, PGPPrivateKey masterPrivateKey,
PGPPrivateKey subPrivateKey, PGPPublicKey pKey, int flags, long expiry) PGPSignatureGenerator subSigGen, PGPPrivateKey subPrivateKey, PGPPublicKey pKey,
int flags, long expiry)
throws IOException, PGPException, SignatureException { throws IOException, PGPException, SignatureException {
PGPSignatureSubpacketGenerator unhashedPacketsGen = new PGPSignatureSubpacketGenerator(); PGPSignatureSubpacketGenerator unhashedPacketsGen = new PGPSignatureSubpacketGenerator();
@@ -1534,10 +1563,6 @@ public class PgpKeyOperation {
// cross-certify signing keys // cross-certify signing keys
PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator(); PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
subHashedPacketsGen.setSignatureCreationTime(false, creationTime); subHashedPacketsGen.setSignatureCreationTime(false, creationTime);
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
pKey.getAlgorithm(), PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
PGPSignatureGenerator subSigGen = new PGPSignatureGenerator(signerBuilder);
subSigGen.init(PGPSignature.PRIMARYKEY_BINDING, subPrivateKey); subSigGen.init(PGPSignature.PRIMARYKEY_BINDING, subPrivateKey);
subSigGen.setHashedSubpackets(subHashedPacketsGen.generate()); subSigGen.setHashedSubpackets(subHashedPacketsGen.generate());
PGPSignature certification = subSigGen.generateCertification(masterPublicKey, pKey); PGPSignature certification = subSigGen.generateCertification(masterPublicKey, pKey);

View File

@@ -53,7 +53,7 @@ import java.io.IOException;
*/ */
public class KeychainDatabase extends SQLiteOpenHelper { public class KeychainDatabase extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "openkeychain.db"; private static final String DATABASE_NAME = "openkeychain.db";
private static final int DATABASE_VERSION = 10; private static final int DATABASE_VERSION = 11;
static Boolean apgHack = false; static Boolean apgHack = false;
private Context mContext; private Context mContext;
@@ -274,6 +274,12 @@ public class KeychainDatabase extends SQLiteOpenHelper {
db.execSQL(CREATE_CERTS); db.execSQL(CREATE_CERTS);
case 10: case 10:
// do nothing here, just consolidate // do nothing here, just consolidate
case 11:
db.execSQL("DELETE FROM api_accounts WHERE key_id BETWEEN 0 AND 3");
if (oldVersion == 10) {
// no consolidate if we are updating from 10, we're just here for the api_accounts fix
return;
}
} }
@@ -297,10 +303,11 @@ public class KeychainDatabase extends SQLiteOpenHelper {
// It's the Java way =( // It's the Java way =(
String[] dbs = context.databaseList(); String[] dbs = context.databaseList();
for (String db : dbs) { for (String db : dbs) {
if (db.equals("apg.db")) { if ("apg.db".equals(db)) {
hasApgDb = true; hasApgDb = true;
} else if (db.equals("apg_old.db")) { } else if ("apg_old.db".equals(db)) {
Log.d(Constants.TAG, "Found apg_old.db, delete it!"); Log.d(Constants.TAG, "Found apg_old.db, delete it!");
// noinspection ResultOfMethodCallIgnored - if it doesn't happen, it doesn't happen.
context.getDatabasePath("apg_old.db").delete(); context.getDatabasePath("apg_old.db").delete();
} }
} }
@@ -384,7 +391,7 @@ public class KeychainDatabase extends SQLiteOpenHelper {
} }
} }
// delete old database // noinspection ResultOfMethodCallIgnored - not much we can do if this doesn't work
context.getDatabasePath("apg.db").delete(); context.getDatabasePath("apg.db").delete();
} }
@@ -416,6 +423,7 @@ public class KeychainDatabase extends SQLiteOpenHelper {
} else { } else {
in = context.getDatabasePath(DATABASE_NAME); in = context.getDatabasePath(DATABASE_NAME);
out = context.getDatabasePath("debug_backup.db"); out = context.getDatabasePath("debug_backup.db");
// noinspection ResultOfMethodCallIgnored - this is a pure debug feature, anyways
out.createNewFile(); out.createNewFile();
} }
if (!in.canRead()) { if (!in.canRead()) {

View File

@@ -324,16 +324,15 @@ public class CertifyKeyFragment
} }
@Override @Override
public void onCryptoOperationSuccess(CertifyResult result) { public void onQueuedOperationSuccess(CertifyResult result) {
// protected by Queueing*Fragment
Activity activity = getActivity();
Intent intent = new Intent(); Intent intent = new Intent();
intent.putExtra(CertifyResult.EXTRA_RESULT, result); intent.putExtra(CertifyResult.EXTRA_RESULT, result);
getActivity().setResult(Activity.RESULT_OK, intent); activity.setResult(Activity.RESULT_OK, intent);
getActivity().finish(); activity.finish();
}
@Override
public void onCryptoOperationCancelled() {
super.onCryptoOperationCancelled();
} }
} }

View File

@@ -225,6 +225,33 @@ public class CreateKeyFinalFragment extends Fragment {
} }
} }
} }
// handle queued actions
if (mQueuedFinishResult != null) {
finishWithResult(mQueuedFinishResult);
return;
}
if (mQueuedDisplayResult != null) {
try {
displayResult(mQueuedDisplayResult);
} finally {
// clear after operation, note that this may drop the operation if it didn't
// work when called from here!
mQueuedDisplayResult = null;
}
}
if (mQueuedSaveKeyResult != null) {
try {
uploadKey(mQueuedSaveKeyResult);
} finally {
// see above
mQueuedSaveKeyResult = null;
}
}
} }
private void createKey() { private void createKey() {
@@ -433,35 +460,4 @@ public class CreateKeyFinalFragment extends Fragment {
activity.finish(); activity.finish();
} }
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// there may be queued actions from when we weren't attached to an activity!
if (mQueuedFinishResult != null) {
finishWithResult(mQueuedFinishResult);
return;
}
if (mQueuedDisplayResult != null) {
try {
displayResult(mQueuedDisplayResult);
} finally {
// clear after operation, note that this may drop the operation if it didn't
// work when called from here!
mQueuedDisplayResult = null;
}
}
if (mQueuedSaveKeyResult != null) {
try {
uploadKey(mQueuedSaveKeyResult);
} finally {
// see above
mQueuedSaveKeyResult = null;
}
}
}
} }

View File

@@ -17,6 +17,7 @@
package org.sufficientlysecure.keychain.ui; package org.sufficientlysecure.keychain.ui;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.ArrayList; import java.util.ArrayList;
@@ -39,7 +40,7 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.service.ImportKeyringParcel; import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction; import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction;
import org.sufficientlysecure.keychain.ui.CreateKeyActivity.NfcListenerFragment; import org.sufficientlysecure.keychain.ui.CreateKeyActivity.NfcListenerFragment;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationFragment; import org.sufficientlysecure.keychain.ui.base.QueueingCryptoOperationFragment;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.ParcelableProxy; import org.sufficientlysecure.keychain.util.ParcelableProxy;
import org.sufficientlysecure.keychain.util.Preferences; import org.sufficientlysecure.keychain.util.Preferences;
@@ -47,7 +48,7 @@ import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
public class CreateYubiKeyImportFragment public class CreateYubiKeyImportFragment
extends CryptoOperationFragment<ImportKeyringParcel, ImportKeyResult> extends QueueingCryptoOperationFragment<ImportKeyringParcel, ImportKeyResult>
implements NfcListenerFragment { implements NfcListenerFragment {
private static final String ARG_FINGERPRINT = "fingerprint"; private static final String ARG_FINGERPRINT = "fingerprint";
@@ -246,14 +247,17 @@ public class CreateYubiKeyImportFragment
} }
@Override @Override
public void onCryptoOperationSuccess(ImportKeyResult result) { public void onQueuedOperationSuccess(ImportKeyResult result) {
long[] masterKeyIds = result.getImportedMasterKeyIds(); long[] masterKeyIds = result.getImportedMasterKeyIds();
if (masterKeyIds.length == 0) { if (masterKeyIds.length == 0) {
super.onCryptoOperationError(result); super.onCryptoOperationError(result);
return; return;
} }
Intent intent = new Intent(getActivity(), ViewKeyActivity.class); // null-protected from Queueing*Fragment
Activity activity = getActivity();
Intent intent = new Intent(activity, ViewKeyActivity.class);
// use the imported masterKeyId, not the one from the yubikey, because // use the imported masterKeyId, not the one from the yubikey, because
// that one might* just have been a subkey of the imported key // that one might* just have been a subkey of the imported key
intent.setData(KeyRings.buildGenericKeyRingUri(masterKeyIds[0])); intent.setData(KeyRings.buildGenericKeyRingUri(masterKeyIds[0]));
@@ -262,6 +266,6 @@ public class CreateYubiKeyImportFragment
intent.putExtra(ViewKeyActivity.EXTRA_NFC_USER_ID, mNfcUserId); intent.putExtra(ViewKeyActivity.EXTRA_NFC_USER_ID, mNfcUserId);
intent.putExtra(ViewKeyActivity.EXTRA_NFC_FINGERPRINTS, mNfcFingerprints); intent.putExtra(ViewKeyActivity.EXTRA_NFC_FINGERPRINTS, mNfcFingerprints);
startActivity(intent); startActivity(intent);
getActivity().finish(); activity.finish();
} }
} }

View File

@@ -49,13 +49,7 @@ public class DecryptActivity extends BaseActivity {
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setFullScreenDialogClose(new View.OnClickListener() { setFullScreenDialogClose(Activity.RESULT_CANCELED, false);
@Override
public void onClick(View v) {
setResult(Activity.RESULT_CANCELED);
finish();
}
}, false);
// Handle intent actions // Handle intent actions
handleActions(savedInstanceState, getIntent()); handleActions(savedInstanceState, getIntent());

View File

@@ -66,10 +66,10 @@ import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.TemporaryStorageProvider; import org.sufficientlysecure.keychain.provider.TemporaryStorageProvider;
// this import NEEDS to be above the ViewModel one, or it won't compile! (as of 06/06/15) // this import NEEDS to be above the ViewModel one, or it won't compile! (as of 06/06/15)
import org.sufficientlysecure.keychain.ui.base.QueueingCryptoOperationFragment;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.StatusHolder; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.StatusHolder;
import org.sufficientlysecure.keychain.ui.DecryptListFragment.DecryptFilesAdapter.ViewModel; import org.sufficientlysecure.keychain.ui.DecryptListFragment.DecryptFilesAdapter.ViewModel;
import org.sufficientlysecure.keychain.ui.adapter.SpacesItemDecoration; import org.sufficientlysecure.keychain.ui.adapter.SpacesItemDecoration;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationFragment;
import org.sufficientlysecure.keychain.ui.util.FormattingUtils; import org.sufficientlysecure.keychain.ui.util.FormattingUtils;
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;
@@ -80,7 +80,7 @@ import org.sufficientlysecure.keychain.util.ParcelableHashMap;
public class DecryptListFragment public class DecryptListFragment
extends CryptoOperationFragment<PgpDecryptVerifyInputParcel,DecryptVerifyResult> extends QueueingCryptoOperationFragment<PgpDecryptVerifyInputParcel,DecryptVerifyResult>
implements OnMenuItemClickListener { implements OnMenuItemClickListener {
public static final String ARG_INPUT_URIS = "input_uris"; public static final String ARG_INPUT_URIS = "input_uris";
@@ -195,15 +195,6 @@ public class DecryptListFragment
cryptoOperation(); cryptoOperation();
} }
private String removeEncryptedAppend(String name) {
if (name.endsWith(Constants.FILE_EXTENSION_ASC)
|| name.endsWith(Constants.FILE_EXTENSION_PGP_MAIN)
|| name.endsWith(Constants.FILE_EXTENSION_PGP_ALTERNATE)) {
return name.substring(0, name.length() - 4);
}
return name;
}
private void askForOutputFilename(Uri inputUri, String originalFilename, String mimeType) { private void askForOutputFilename(Uri inputUri, String originalFilename, String mimeType) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
File file = new File(inputUri.getPath()); File file = new File(inputUri.getPath());
@@ -257,7 +248,7 @@ public class DecryptListFragment
return true; return true;
} }
@Override @Override
public void onCryptoOperationError(DecryptVerifyResult result) { public void onQueuedOperationError(DecryptVerifyResult result) {
final Uri uri = mCurrentInputUri; final Uri uri = mCurrentInputUri;
mCurrentInputUri = null; mCurrentInputUri = null;
@@ -267,7 +258,7 @@ public class DecryptListFragment
} }
@Override @Override
public void onCryptoOperationSuccess(DecryptVerifyResult result) { public void onQueuedOperationSuccess(DecryptVerifyResult result) {
Uri uri = mCurrentInputUri; Uri uri = mCurrentInputUri;
mCurrentInputUri = null; mCurrentInputUri = null;

View File

@@ -41,13 +41,7 @@ public class DisplayTextActivity extends BaseActivity {
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setFullScreenDialogClose(new View.OnClickListener() { setFullScreenDialogClose(Activity.RESULT_CANCELED, false);
@Override
public void onClick(View v) {
setResult(Activity.RESULT_CANCELED);
finish();
}
}, false);
// Handle intent actions // Handle intent actions
handleActions(savedInstanceState, getIntent()); handleActions(savedInstanceState, getIntent());

View File

@@ -57,7 +57,7 @@ import org.sufficientlysecure.keychain.ui.adapter.SubkeysAdapter;
import org.sufficientlysecure.keychain.ui.adapter.SubkeysAddedAdapter; import org.sufficientlysecure.keychain.ui.adapter.SubkeysAddedAdapter;
import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter; import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter;
import org.sufficientlysecure.keychain.ui.adapter.UserIdsAddedAdapter; import org.sufficientlysecure.keychain.ui.adapter.UserIdsAddedAdapter;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationFragment; import org.sufficientlysecure.keychain.ui.base.QueueingCryptoOperationFragment;
import org.sufficientlysecure.keychain.ui.dialog.AddSubkeyDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.AddSubkeyDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.AddUserIdDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.AddUserIdDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.EditSubkeyDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.EditSubkeyDialogFragment;
@@ -68,7 +68,7 @@ import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Passphrase; import org.sufficientlysecure.keychain.util.Passphrase;
public class EditKeyFragment extends CryptoOperationFragment<SaveKeyringParcel, OperationResult> public class EditKeyFragment extends QueueingCryptoOperationFragment<SaveKeyringParcel, OperationResult>
implements LoaderManager.LoaderCallbacks<Cursor> { implements LoaderManager.LoaderCallbacks<Cursor> {
public static final String ARG_DATA_URI = "uri"; public static final String ARG_DATA_URI = "uri";
@@ -192,7 +192,7 @@ public class EditKeyFragment extends CryptoOperationFragment<SaveKeyringParcel,
private void loadData(Uri dataUri) { private void loadData(Uri dataUri) {
mDataUri = dataUri; mDataUri = dataUri;
Log.i(Constants.TAG, "mDataUri: " + mDataUri.toString()); Log.i(Constants.TAG, "mDataUri: " + mDataUri);
// load the secret key ring. we do verify here that the passphrase is correct, so cached won't do // load the secret key ring. we do verify here that the passphrase is correct, so cached won't do
try { try {
@@ -618,13 +618,16 @@ public class EditKeyFragment extends CryptoOperationFragment<SaveKeyringParcel,
} }
@Override @Override
public void onCryptoOperationSuccess(OperationResult result) { public void onQueuedOperationSuccess(OperationResult result) {
// null-protected from Queueing*Fragment
Activity activity = getActivity();
// if good -> finish, return result to showkey and display there! // if good -> finish, return result to showkey and display there!
Intent intent = new Intent(); Intent intent = new Intent();
intent.putExtra(OperationResult.EXTRA_RESULT, result); intent.putExtra(OperationResult.EXTRA_RESULT, result);
getActivity().setResult(EditKeyActivity.RESULT_OK, intent); activity.setResult(EditKeyActivity.RESULT_OK, intent);
getActivity().finish(); activity.finish();
} }

View File

@@ -18,6 +18,7 @@
package org.sufficientlysecure.keychain.ui; package org.sufficientlysecure.keychain.ui;
import android.app.Activity;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
@@ -41,12 +42,7 @@ public class EncryptFilesActivity extends EncryptActivity {
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setFullScreenDialogClose(new View.OnClickListener() { setFullScreenDialogClose(Activity.RESULT_OK, false);
@Override
public void onClick(View v) {
finish();
}
}, false);
Intent intent = getIntent(); Intent intent = getIntent();
String action = intent.getAction(); String action = intent.getAction();

View File

@@ -387,13 +387,10 @@ public class EncryptFilesFragment
} }
@Override @Override
public void onCryptoOperationSuccess(final SignEncryptResult result) { public void onQueuedOperationSuccess(final SignEncryptResult result) {
// protected by Queueing*Fragment
FragmentActivity activity = getActivity(); FragmentActivity activity = getActivity();
if (activity == null) {
// it's gone, there's nothing we can do here
return;
}
if (mDeleteAfterEncrypt) { if (mDeleteAfterEncrypt) {
// TODO make behavior coherent here // TODO make behavior coherent here

View File

@@ -18,6 +18,7 @@
package org.sufficientlysecure.keychain.ui; package org.sufficientlysecure.keychain.ui;
import android.app.Activity;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.support.v4.app.FragmentTransaction; import android.support.v4.app.FragmentTransaction;
@@ -39,12 +40,7 @@ public class EncryptTextActivity extends EncryptActivity {
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setFullScreenDialogClose(new View.OnClickListener() { setFullScreenDialogClose(Activity.RESULT_OK, false);
@Override
public void onClick(View v) {
finish();
}
}, false);
Intent intent = getIntent(); Intent intent = getIntent();
String action = intent.getAction(); String action = intent.getAction();

View File

@@ -331,7 +331,7 @@ public class EncryptTextFragment
} }
@Override @Override
public void onCryptoOperationSuccess(SignEncryptResult result) { public void onQueuedOperationSuccess(SignEncryptResult result) {
if (mShareAfterEncrypt) { if (mShareAfterEncrypt) {
// Share encrypted message/file // Share encrypted message/file

View File

@@ -17,6 +17,7 @@
package org.sufficientlysecure.keychain.ui; package org.sufficientlysecure.keychain.ui;
import android.app.Activity;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
@@ -95,6 +96,8 @@ public class ImportKeysActivity extends BaseNfcActivity
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setFullScreenDialogClose(Activity.RESULT_CANCELED, true);
mProxyPrefs = Preferences.getPreferences(this).getProxyPrefs(); mProxyPrefs = Preferences.getPreferences(this).getProxyPrefs();
mImportButton = findViewById(R.id.import_import); mImportButton = findViewById(R.id.import_import);

View File

@@ -257,7 +257,6 @@ public class ImportKeysProxyActivity extends FragmentActivity
Intent data = new Intent(); Intent data = new Intent();
data.putExtras(returnData); data.putExtras(returnData);
returnResult(data); returnResult(data);
return;
} }
@Override @Override

View File

@@ -31,7 +31,6 @@ import android.os.AsyncTask;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.Handler.Callback;
import android.os.Message; import android.os.Message;
import android.os.Messenger; import android.os.Messenger;
import android.provider.ContactsContract; import android.provider.ContactsContract;
@@ -57,7 +56,6 @@ import com.getbase.floatingactionbutton.FloatingActionButton;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing; import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.operations.results.CertifyResult;
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult; import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.pgp.KeyRing; import org.sufficientlysecure.keychain.pgp.KeyRing;
@@ -67,10 +65,7 @@ import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.ImportKeyringParcel; import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
import org.sufficientlysecure.keychain.service.KeychainService;
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
import org.sufficientlysecure.keychain.service.ServiceProgressHandler.MessageStatus; import org.sufficientlysecure.keychain.service.ServiceProgressHandler.MessageStatus;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.ui.base.BaseNfcActivity; import org.sufficientlysecure.keychain.ui.base.BaseNfcActivity;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper; import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
@@ -102,6 +97,8 @@ public class ViewKeyActivity extends BaseNfcActivity implements
static final int REQUEST_QR_FINGERPRINT = 1; static final int REQUEST_QR_FINGERPRINT = 1;
static final int REQUEST_DELETE = 2; static final int REQUEST_DELETE = 2;
static final int REQUEST_EXPORT = 3; static final int REQUEST_EXPORT = 3;
static final int REQUEST_CERTIFY = 4;
public static final String EXTRA_DISPLAY_RESULT = "display_result"; public static final String EXTRA_DISPLAY_RESULT = "display_result";
ProviderHelper mProviderHelper; ProviderHelper mProviderHelper;
@@ -158,6 +155,7 @@ public class ViewKeyActivity extends BaseNfcActivity implements
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
mProviderHelper = new ProviderHelper(this); mProviderHelper = new ProviderHelper(this);
mOperationHelper = new CryptoOperationHelper<>(this, this, null);
setTitle(null); setTitle(null);
@@ -382,37 +380,14 @@ public class ViewKeyActivity extends BaseNfcActivity implements
Intent intent = new Intent(this, CertifyFingerprintActivity.class); Intent intent = new Intent(this, CertifyFingerprintActivity.class);
intent.setData(dataUri); intent.setData(dataUri);
startCertifyIntent(intent); startActivityForResult(intent, REQUEST_CERTIFY);
} }
private void certifyImmediate() { private void certifyImmediate() {
Intent intent = new Intent(this, CertifyKeyActivity.class); Intent intent = new Intent(this, CertifyKeyActivity.class);
intent.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS, new long[]{mMasterKeyId}); intent.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS, new long[]{mMasterKeyId});
startCertifyIntent(intent); startActivityForResult(intent, REQUEST_CERTIFY);
}
private void startCertifyIntent(Intent intent) {
// Message is received after signing is done in KeychainService
ServiceProgressHandler saveHandler = new ServiceProgressHandler(this) {
@Override
public void handleMessage(Message message) {
// handle messages by standard KeychainIntentServiceHandler first
super.handleMessage(message);
if (message.arg1 == MessageStatus.OKAY.ordinal()) {
Bundle data = message.getData();
CertifyResult result = data.getParcelable(CertifyResult.EXTRA_RESULT);
result.createNotify(ViewKeyActivity.this).show();
}
}
};
// Create a new Messenger for the communication back
Messenger messenger = new Messenger(saveHandler);
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
startActivityForResult(intent, 0);
} }
private void showQrCodeDialog() { private void showQrCodeDialog() {
@@ -482,49 +457,58 @@ public class ViewKeyActivity extends BaseNfcActivity implements
@Override @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) { protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (mOperationHelper != null) { if (mOperationHelper.handleActivityResult(requestCode, resultCode, data)) {
mOperationHelper.handleActivityResult(requestCode, resultCode, data);
}
if (requestCode == REQUEST_QR_FINGERPRINT && resultCode == Activity.RESULT_OK) {
// If there is an EXTRA_RESULT, that's an error. Just show it.
if (data.hasExtra(OperationResult.EXTRA_RESULT)) {
OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT);
result.createNotify(this).show();
return;
}
String fp = data.getStringExtra(ImportKeysProxyActivity.EXTRA_FINGERPRINT);
if (fp == null) {
Notify.create(this, "Error scanning fingerprint!",
Notify.LENGTH_LONG, Notify.Style.ERROR).show();
return;
}
if (mFingerprint.equalsIgnoreCase(fp)) {
certifyImmediate();
} else {
Notify.create(this, "Fingerprints did not match!",
Notify.LENGTH_LONG, Notify.Style.ERROR).show();
}
return; return;
} }
if (requestCode == REQUEST_DELETE && resultCode == Activity.RESULT_OK) { if (resultCode != Activity.RESULT_OK) {
deleteKey(); return;
} }
if (requestCode == REQUEST_EXPORT && resultCode == Activity.RESULT_OK) { switch (requestCode) {
exportToFile(mDataUri, mProviderHelper); case REQUEST_QR_FINGERPRINT: {
// If there is an EXTRA_RESULT, that's an error. Just show it.
if (data.hasExtra(OperationResult.EXTRA_RESULT)) {
OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT);
result.createNotify(this).show();
return;
}
String fp = data.getStringExtra(ImportKeysProxyActivity.EXTRA_FINGERPRINT);
if (fp == null) {
Notify.create(this, R.string.error_scan_fp, Notify.LENGTH_LONG, Style.ERROR).show();
return;
}
if (mFingerprint.equalsIgnoreCase(fp)) {
certifyImmediate();
} else {
Notify.create(this, R.string.error_scan_match, Notify.LENGTH_LONG, Style.ERROR).show();
}
return;
}
case REQUEST_DELETE: {
deleteKey();
return;
}
case REQUEST_EXPORT: {
exportToFile(mDataUri, mProviderHelper);
return;
}
case REQUEST_CERTIFY: {
if (data.hasExtra(OperationResult.EXTRA_RESULT)) {
OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT);
result.createNotify(this).show();
}
return;
}
} }
if (data != null && data.hasExtra(OperationResult.EXTRA_RESULT)) { super.onActivityResult(requestCode, resultCode, data);
OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT);
result.createNotify(this).show();
} else {
super.onActivityResult(requestCode, resultCode, data);
}
} }
@Override @Override
@@ -863,6 +847,7 @@ public class ViewKeyActivity extends BaseNfcActivity implements
mActionNfc.setVisibility(View.GONE); mActionNfc.setVisibility(View.GONE);
} }
mFab.setVisibility(View.VISIBLE); mFab.setVisibility(View.VISIBLE);
// noinspection deprecation (no getDrawable with theme at current minApi level 15!)
mFab.setIconDrawable(getResources().getDrawable(R.drawable.ic_repeat_white_24dp)); mFab.setIconDrawable(getResources().getDrawable(R.drawable.ic_repeat_white_24dp));
} else { } else {
mActionEncryptFile.setVisibility(View.VISIBLE); mActionEncryptFile.setVisibility(View.VISIBLE);
@@ -952,7 +937,6 @@ public class ViewKeyActivity extends BaseNfcActivity implements
mKeyserver = cloudPrefs.keyserver; mKeyserver = cloudPrefs.keyserver;
} }
mOperationHelper = new CryptoOperationHelper<>(this, this, null);
mOperationHelper.cryptoOperation(); mOperationHelper.cryptoOperation();
} }

View File

@@ -18,6 +18,7 @@
package org.sufficientlysecure.keychain.ui; package org.sufficientlysecure.keychain.ui;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.Arrays; import java.util.Arrays;
@@ -39,12 +40,12 @@ import org.sufficientlysecure.keychain.operations.results.PromoteKeyResult;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType; import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType;
import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
import org.sufficientlysecure.keychain.service.PromoteKeyringParcel; import org.sufficientlysecure.keychain.service.PromoteKeyringParcel;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationFragment; import org.sufficientlysecure.keychain.ui.base.QueueingCryptoOperationFragment;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
public class ViewKeyYubiKeyFragment public class ViewKeyYubiKeyFragment
extends CryptoOperationFragment<PromoteKeyringParcel, PromoteKeyResult> extends QueueingCryptoOperationFragment<PromoteKeyringParcel, PromoteKeyResult>
implements LoaderCallbacks<Cursor> { implements LoaderCallbacks<Cursor> {
public static final String ARG_MASTER_KEY_ID = "master_key_id"; public static final String ARG_MASTER_KEY_ID = "master_key_id";
@@ -76,7 +77,7 @@ public class ViewKeyYubiKeyFragment
} }
public ViewKeyYubiKeyFragment() { public ViewKeyYubiKeyFragment() {
super(R.string.progress_processing); super(null);
} }
@Override @Override
@@ -214,7 +215,8 @@ public class ViewKeyYubiKeyFragment
} }
@Override @Override
public void onCryptoOperationSuccess(PromoteKeyResult result) { public void onQueuedOperationSuccess(PromoteKeyResult result) {
result.createNotify(getActivity()).show(); result.createNotify(getActivity()).show();
} }
} }

View File

@@ -87,9 +87,7 @@ public abstract class BaseActivity extends AppCompatActivity {
mToolbar.setNavigationOnClickListener(cancelOnClickListener); mToolbar.setNavigationOnClickListener(cancelOnClickListener);
} }
/** /** Close button only */
* Close button only
*/
protected void setFullScreenDialogClose(View.OnClickListener cancelOnClickListener, boolean white) { protected void setFullScreenDialogClose(View.OnClickListener cancelOnClickListener, boolean white) {
if (white) { if (white) {
setActionBarIcon(R.drawable.ic_close_white_24dp); setActionBarIcon(R.drawable.ic_close_white_24dp);
@@ -104,6 +102,17 @@ public abstract class BaseActivity extends AppCompatActivity {
setFullScreenDialogClose(cancelOnClickListener, true); setFullScreenDialogClose(cancelOnClickListener, true);
} }
/** Close button only, with finish-action and given return status, white. */
protected void setFullScreenDialogClose(final int result, boolean white) {
setFullScreenDialogClose(new View.OnClickListener() {
@Override
public void onClick(View v) {
setResult(result);
finish();
}
}, white);
}
/** /**
* Inflate custom design with two buttons using drawables. * Inflate custom design with two buttons using drawables.
* This does not conform to the Material Design Guidelines, but we deviate here as this is used * This does not conform to the Material Design Guidelines, but we deviate here as this is used

View File

@@ -4,12 +4,11 @@ package org.sufficientlysecure.keychain.ui.base;
import android.os.Bundle; import android.os.Bundle;
import android.os.Parcelable; import android.os.Parcelable;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.operations.results.OperationResult;
public abstract class CachingCryptoOperationFragment <T extends Parcelable, S extends OperationResult> public abstract class CachingCryptoOperationFragment <T extends Parcelable, S extends OperationResult>
extends CryptoOperationFragment<T, S> { extends QueueingCryptoOperationFragment<T, S> {
public static final String ARG_CACHED_ACTIONS = "cached_actions"; public static final String ARG_CACHED_ACTIONS = "cached_actions";
@@ -32,12 +31,14 @@ public abstract class CachingCryptoOperationFragment <T extends Parcelable, S ex
} }
@Override @Override
public void onCryptoOperationSuccess(S result) { public void onQueuedOperationSuccess(S result) {
super.onCryptoOperationSuccess(result);
mCachedActionsParcel = null; mCachedActionsParcel = null;
} }
@Override @Override
public void onCryptoOperationError(S result) { public void onQueuedOperationError(S result) {
super.onCryptoOperationError(result);
mCachedActionsParcel = null; mCachedActionsParcel = null;
} }

View File

@@ -46,7 +46,7 @@ import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
* @see KeychainService * @see KeychainService
* *
*/ */
public abstract class CryptoOperationFragment<T extends Parcelable, S extends OperationResult> abstract class CryptoOperationFragment<T extends Parcelable, S extends OperationResult>
extends Fragment implements CryptoOperationHelper.Callback<T, S> { extends Fragment implements CryptoOperationHelper.Callback<T, S> {
final private CryptoOperationHelper<T, S> mOperationHelper; final private CryptoOperationHelper<T, S> mOperationHelper;

View File

@@ -208,12 +208,9 @@ public class CryptoOperationHelper<T extends Parcelable, S extends OperationResu
return true; return true;
} }
} }
default: {
return false;
}
} }
return true;
return false;
} }
protected void dismissProgress() { protected void dismissProgress() {

View File

@@ -0,0 +1,92 @@
package org.sufficientlysecure.keychain.ui.base;
import android.os.Bundle;
import android.os.Parcelable;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
/** CryptoOperationFragment which calls crypto operation results only while
* attached to Activity.
*
* This subclass of CryptoOperationFragment substitutes the onCryptoOperation*
* methods for onQueuedOperation* ones, which are ensured to be called while
* the fragment is attached to an Activity, possibly delaying the call until
* the Fragment is re-attached.
*
* TODO merge this functionality into CryptoOperationFragment?
*
* @see CryptoOperationFragment
*/
public abstract class QueueingCryptoOperationFragment<T extends Parcelable, S extends OperationResult>
extends CryptoOperationFragment<T,S> {
public static final String ARG_QUEUED_RESULT = "queued_result";
private S mQueuedResult;
public QueueingCryptoOperationFragment() {
super();
}
public QueueingCryptoOperationFragment(Integer initialProgressMsg) {
super(initialProgressMsg);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (mQueuedResult != null) {
try {
if (mQueuedResult.success()) {
onQueuedOperationSuccess(mQueuedResult);
} else {
onQueuedOperationError(mQueuedResult);
}
} finally {
mQueuedResult = null;
}
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelable(ARG_QUEUED_RESULT, mQueuedResult);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
mQueuedResult = savedInstanceState.getParcelable(ARG_QUEUED_RESULT);
}
}
public abstract void onQueuedOperationSuccess(S result);
public void onQueuedOperationError(S result) {
super.onCryptoOperationError(result);
}
@Override
final public void onCryptoOperationSuccess(S result) {
if (getActivity() == null) {
mQueuedResult = result;
return;
}
onQueuedOperationSuccess(result);
}
@Override
final public void onCryptoOperationError(S result) {
if (getActivity() == null) {
mQueuedResult = result;
return;
}
onQueuedOperationError(result);
}
}

View File

@@ -24,6 +24,7 @@ import android.os.Bundle;
import android.os.Message; import android.os.Message;
import android.os.Messenger; import android.os.Messenger;
import android.os.RemoteException; import android.os.RemoteException;
import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment; import android.support.v4.app.DialogFragment;
import android.text.format.DateFormat; import android.text.format.DateFormat;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@@ -36,6 +37,8 @@ import android.widget.TextView;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.ui.util.Notify.Style;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import java.util.Calendar; import java.util.Calendar;
@@ -72,16 +75,18 @@ public class EditSubkeyExpiryDialogFragment extends DialogFragment {
/** /**
* Creates dialog * Creates dialog
*/ */
@NonNull
@Override @Override
public Dialog onCreateDialog(Bundle savedInstanceState) { public Dialog onCreateDialog(Bundle savedInstanceState) {
final Activity activity = getActivity(); Activity activity = getActivity();
mMessenger = getArguments().getParcelable(ARG_MESSENGER); mMessenger = getArguments().getParcelable(ARG_MESSENGER);
long creation = getArguments().getLong(ARG_CREATION); long creation = getArguments().getLong(ARG_CREATION);
long expiry = getArguments().getLong(ARG_EXPIRY); long expiry = getArguments().getLong(ARG_EXPIRY);
Calendar creationCal = Calendar.getInstance(TimeZone.getTimeZone("UTC")); final Calendar creationCal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
creationCal.setTime(new Date(creation * 1000)); creationCal.setTime(new Date(creation * 1000));
final Calendar expiryCal = Calendar.getInstance(TimeZone.getTimeZone("UTC")); Calendar expiryCal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
expiryCal.setTime(new Date(expiry * 1000)); expiryCal.setTime(new Date(expiry * 1000));
// date picker works with default time zone, we need to convert from UTC to default timezone // date picker works with default time zone, we need to convert from UTC to default timezone
@@ -175,10 +180,13 @@ public class EditSubkeyExpiryDialogFragment extends DialogFragment {
selectedCal.setTimeZone(TimeZone.getTimeZone("UTC")); selectedCal.setTimeZone(TimeZone.getTimeZone("UTC"));
long numDays = (selectedCal.getTimeInMillis() / 86400000) long numDays = (selectedCal.getTimeInMillis() / 86400000)
- (expiryCal.getTimeInMillis() / 86400000); - (creationCal.getTimeInMillis() / 86400000);
if (numDays <= 0) { if (numDays <= 0) {
Log.e(Constants.TAG, "Should not happen! Expiry num of days <= 0!"); Activity activity = getActivity();
throw new RuntimeException(); if (activity != null) {
Notify.create(activity, R.string.error_expiry_past, Style.ERROR).show();
}
return;
} }
expiry = selectedCal.getTime().getTime() / 1000; expiry = selectedCal.getTime().getTime() / 1000;
} }

View File

@@ -976,6 +976,7 @@
<!-- modifySecretKeyRing --> <!-- modifySecretKeyRing -->
<string name="msg_mr">"Modifying keyring %s"</string> <string name="msg_mr">"Modifying keyring %s"</string>
<string name="msg_mf_divert">"Will divert to smart card for crypto operations"</string> <string name="msg_mf_divert">"Will divert to smart card for crypto operations"</string>
<string name="msg_mf_error_divert_newsub">"Creation of new subkeys is not supported for 'divert-to-card' primary keys!"</string>
<string name="msg_mf_error_divert_serial">"The serial number of a 'divert-to-card' key must be 16 bytes! This is a programming error, please file a bug report!"</string> <string name="msg_mf_error_divert_serial">"The serial number of a 'divert-to-card' key must be 16 bytes! This is a programming error, please file a bug report!"</string>
<string name="msg_mf_error_encode">"Encoding exception!"</string> <string name="msg_mf_error_encode">"Encoding exception!"</string>
<string name="msg_mf_error_fingerprint">"Actual key fingerprint does not match the expected one!"</string> <string name="msg_mf_error_fingerprint">"Actual key fingerprint does not match the expected one!"</string>
@@ -991,6 +992,7 @@
<string name="msg_mf_error_passphrase_master">"Fatal error decrypting master key! This is likely a programming error, please file a bug report!"</string> <string name="msg_mf_error_passphrase_master">"Fatal error decrypting master key! This is likely a programming error, please file a bug report!"</string>
<string name="msg_mf_error_pgp">"Internal OpenPGP error!"</string> <string name="msg_mf_error_pgp">"Internal OpenPGP error!"</string>
<string name="msg_mf_error_sig">"Signature exception!"</string> <string name="msg_mf_error_sig">"Signature exception!"</string>
<string name="msg_mf_error_sub_stripped">"Cannot modify stripped subkey %s!"</string>
<string name="msg_mf_error_subkey_missing">"Tried to operate on missing subkey %s!"</string> <string name="msg_mf_error_subkey_missing">"Tried to operate on missing subkey %s!"</string>
<string name="msg_mf_error_conflicting_nfc_commands">"Cannot move key to smart card in same operation that creates an on-card signature."</string> <string name="msg_mf_error_conflicting_nfc_commands">"Cannot move key to smart card in same operation that creates an on-card signature."</string>
<string name="msg_mf_error_duplicate_keytocard_for_slot">"Smart card supports only one slot per key type."</string> <string name="msg_mf_error_duplicate_keytocard_for_slot">"Smart card supports only one slot per key type."</string>
@@ -1432,5 +1434,8 @@
<string name="file_delete_exception">"Original file could not be deleted!"</string> <string name="file_delete_exception">"Original file could not be deleted!"</string>
<string name="error_clipboard_empty">"Clipboard is empty!"</string> <string name="error_clipboard_empty">"Clipboard is empty!"</string>
<string name="error_clipboard_copy">"Error copying data to clipboard!"</string> <string name="error_clipboard_copy">"Error copying data to clipboard!"</string>
<string name="error_scan_fp">"Error scanning fingerprint!"</string>
<string name="error_scan_match">"Fingerprints did not match!"</string>
<string name="error_expiry_past">"Expiry date is in the past!"</string>
</resources> </resources>

View File

@@ -94,11 +94,11 @@ public class PgpKeyOperationTest {
SaveKeyringParcel parcel = new SaveKeyringParcel(); SaveKeyringParcel parcel = new SaveKeyringParcel();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
Algorithm.RSA, 1024, null, KeyFlags.CERTIFY_OTHER, 0L)); Algorithm.DSA, 1024, null, KeyFlags.CERTIFY_OTHER, 0L));
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
Algorithm.DSA, 1024, null, KeyFlags.SIGN_DATA, 0L)); Algorithm.RSA, 2048, null, KeyFlags.SIGN_DATA, 0L));
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
Algorithm.RSA, 2048, null, KeyFlags.ENCRYPT_COMMS, 0L)); Algorithm.RSA, 1024, null, KeyFlags.ENCRYPT_COMMS, 0L));
parcel.mAddUserIds.add("twi"); parcel.mAddUserIds.add("twi");
parcel.mAddUserIds.add("pink"); parcel.mAddUserIds.add("pink");
@@ -821,6 +821,15 @@ public class PgpKeyOperationTest {
Assert.assertEquals("new packet should have GNU_DUMMY protection mode stripped", Assert.assertEquals("new packet should have GNU_DUMMY protection mode stripped",
S2K.GNU_PROTECTION_MODE_NO_PRIVATE_KEY, ((SecretKeyPacket) p).getS2K().getProtectionMode()); S2K.GNU_PROTECTION_MODE_NO_PRIVATE_KEY, ((SecretKeyPacket) p).getS2K().getProtectionMode());
} }
{ // trying to edit a subkey with signing capability should fail
parcel.reset();
parcel.mChangeSubKeys.add(new SubkeyChange(keyId, true));
assertModifyFailure("subkey modification for signing-enabled but stripped subkey should fail",
modified, parcel, LogType.MSG_MF_ERROR_SUB_STRIPPED);
}
} }
@Test @Test
@@ -829,7 +838,7 @@ public class PgpKeyOperationTest {
UncachedKeyRing modified; UncachedKeyRing modified;
{ // keytocard should fail with BAD_NFC_SIZE when presented with the RSA-1024 key { // keytocard should fail with BAD_NFC_SIZE when presented with the RSA-1024 key
long keyId = KeyringTestingHelper.getSubkeyId(ring, 0); long keyId = KeyringTestingHelper.getSubkeyId(ring, 2);
parcel.reset(); parcel.reset();
parcel.mChangeSubKeys.add(new SubkeyChange(keyId, false, true)); parcel.mChangeSubKeys.add(new SubkeyChange(keyId, false, true));
@@ -838,7 +847,7 @@ public class PgpKeyOperationTest {
} }
{ // keytocard should fail with BAD_NFC_ALGO when presented with the DSA-1024 key { // keytocard should fail with BAD_NFC_ALGO when presented with the DSA-1024 key
long keyId = KeyringTestingHelper.getSubkeyId(ring, 1); long keyId = KeyringTestingHelper.getSubkeyId(ring, 0);
parcel.reset(); parcel.reset();
parcel.mChangeSubKeys.add(new SubkeyChange(keyId, false, true)); parcel.mChangeSubKeys.add(new SubkeyChange(keyId, false, true));
@@ -846,9 +855,10 @@ public class PgpKeyOperationTest {
parcel, cryptoInput, LogType.MSG_MF_ERROR_BAD_NFC_ALGO); parcel, cryptoInput, LogType.MSG_MF_ERROR_BAD_NFC_ALGO);
} }
long keyId = KeyringTestingHelper.getSubkeyId(ring, 1);
{ // keytocard should return a pending NFC_MOVE_KEY_TO_CARD result when presented with the RSA-2048 { // keytocard should return a pending NFC_MOVE_KEY_TO_CARD result when presented with the RSA-2048
// key, and then make key divert-to-card when it gets a serial in the cryptoInputParcel. // key, and then make key divert-to-card when it gets a serial in the cryptoInputParcel.
long keyId = KeyringTestingHelper.getSubkeyId(ring, 2);
parcel.reset(); parcel.reset();
parcel.mChangeSubKeys.add(new SubkeyChange(keyId, false, true)); parcel.mChangeSubKeys.add(new SubkeyChange(keyId, false, true));
@@ -880,7 +890,19 @@ public class PgpKeyOperationTest {
S2K.GNU_PROTECTION_MODE_DIVERT_TO_CARD, ((SecretKeyPacket) p).getS2K().getProtectionMode()); S2K.GNU_PROTECTION_MODE_DIVERT_TO_CARD, ((SecretKeyPacket) p).getS2K().getProtectionMode());
Assert.assertArrayEquals("new packet should have correct serial number as iv", Assert.assertArrayEquals("new packet should have correct serial number as iv",
serial, ((SecretKeyPacket) p).getIV()); serial, ((SecretKeyPacket) p).getIV());
}
{ // editing a signing subkey requires a primary key binding sig -> pendinginput
parcel.reset();
parcel.mChangeSubKeys.add(new SubkeyChange(keyId, true));
CanonicalizedSecretKeyRing secretRing =
new CanonicalizedSecretKeyRing(modified.getEncoded(), false, 0);
PgpKeyOperation op = new PgpKeyOperation(null);
PgpEditKeyResult result = op.modifySecretKeyRing(secretRing, cryptoInput, parcel);
Assert.assertTrue("keytocard operation should be pending", result.isPending());
Assert.assertEquals("required input should be RequiredInputType.NFC_SIGN",
RequiredInputType.NFC_SIGN, result.getRequiredInputParcel().mType);
} }
} }

View File

@@ -558,8 +558,9 @@ public class UncachedKeyringCanonicalizeTest {
PGPSignature cert = PgpKeyOperation.generateSubkeyBindingSignature( PGPSignature cert = PgpKeyOperation.generateSubkeyBindingSignature(
PgpKeyOperation.getSignatureGenerator(masterSecretKey.getSecretKey(), cryptoInput), PgpKeyOperation.getSignatureGenerator(masterSecretKey.getSecretKey(), cryptoInput),
cryptoInput.getSignatureTime(), cryptoInput.getSignatureTime(),
masterPublicKey, masterSecretKey.getPrivateKey(), masterSecretKey.getPrivateKey(), masterPublicKey, masterSecretKey.getPrivateKey(),
masterPublicKey, masterSecretKey.getKeyUsage(), 0); PgpKeyOperation.getSignatureGenerator(masterSecretKey.getSecretKey(), null),
masterSecretKey.getPrivateKey(), masterPublicKey, masterSecretKey.getKeyUsage(), 0);
PGPPublicKey subPubKey = PGPPublicKey.addSubkeyBindingCertification(masterPublicKey, cert); PGPPublicKey subPubKey = PGPPublicKey.addSubkeyBindingCertification(masterPublicKey, cert);
PGPSecretKey sKey; PGPSecretKey sKey;