Merge remote-tracking branch 'origin/master'

This commit is contained in:
Vincent Breitmoser
2015-07-15 21:14:53 +02:00
55 changed files with 1455 additions and 448 deletions

View File

@@ -682,6 +682,12 @@
<activity
android:name=".ui.PassphraseDialogActivity"
android:theme="@android:style/Theme.NoDisplay" />
<activity
android:name=".ui.RetryUploadDialogActivity"
android:theme="@android:style/Theme.NoDisplay" />
<activity
android:name=".ui.DeleteKeyDialogActivity"
android:theme="@android:style/Theme.NoDisplay" />
<activity
android:name=".ui.OrbotRequiredDialogActivity"
android:theme="@android:style/Theme.NoDisplay" />

View File

@@ -103,7 +103,10 @@ public class CertifyOperation extends BaseOperation<CertifyActionsParcel> {
if (passphrase == null) {
return new CertifyResult(log,
RequiredInputParcel.createRequiredSignPassphrase(
certificationKey.getKeyId(), certificationKey.getKeyId(), null)
certificationKey.getKeyId(),
certificationKey.getKeyId(),
null),
cryptoInput
);
}
break;
@@ -189,7 +192,7 @@ public class CertifyOperation extends BaseOperation<CertifyActionsParcel> {
if (!allRequiredInput.isEmpty()) {
log.add(LogType.MSG_CRT_NFC_RETURN, 1);
return new CertifyResult(log, allRequiredInput.build());
return new CertifyResult(log, allRequiredInput.build(), cryptoInput);
}
log.add(LogType.MSG_CRT_SAVING, 1);
@@ -211,7 +214,7 @@ public class CertifyOperation extends BaseOperation<CertifyActionsParcel> {
// explicit proxy not set
if (!OrbotHelper.isOrbotInRequiredState(mContext)) {
return new CertifyResult(null,
RequiredInputParcel.createOrbotRequiredOperation());
RequiredInputParcel.createOrbotRequiredOperation(), cryptoInput);
}
proxy = Preferences.getPreferences(mContext).getProxyPrefs()
.parcelableProxy.getProxy();

View File

@@ -22,6 +22,7 @@ import android.support.annotation.NonNull;
import org.sufficientlysecure.keychain.operations.results.ConsolidateResult;
import org.sufficientlysecure.keychain.operations.results.DeleteResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
import org.sufficientlysecure.keychain.pgp.Progressable;
@@ -48,12 +49,17 @@ public class DeleteOperation extends BaseOperation<DeleteKeyringParcel> {
@NonNull
@Override
public DeleteResult execute(DeleteKeyringParcel deleteKeyringParcel,
public OperationResult execute(DeleteKeyringParcel deleteKeyringParcel,
CryptoInputParcel cryptoInputParcel) {
long[] masterKeyIds = deleteKeyringParcel.mMasterKeyIds;
boolean isSecret = deleteKeyringParcel.mIsSecret;
return onlyDeleteKey(masterKeyIds, isSecret);
}
private DeleteResult onlyDeleteKey(long[] masterKeyIds, boolean isSecret) {
OperationLog log = new OperationLog();
if (masterKeyIds.length == 0) {
@@ -113,7 +119,6 @@ public class DeleteOperation extends BaseOperation<DeleteKeyringParcel> {
}
return new DeleteResult(result, log, success, fail);
}
}

View File

@@ -22,7 +22,8 @@ import android.support.annotation.NonNull;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.EditKeyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.operations.results.ExportResult;
import org.sufficientlysecure.keychain.operations.results.InputPendingResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
import org.sufficientlysecure.keychain.operations.results.PgpEditKeyResult;
@@ -34,16 +35,20 @@ import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException;
import org.sufficientlysecure.keychain.service.ContactSyncAdapterService;
import org.sufficientlysecure.keychain.service.ExportKeyringParcel;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.ProgressScaler;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicBoolean;
/** An operation which implements a high level key edit operation.
*
/**
* An operation which implements a high level key edit operation.
* <p/>
* This operation provides a higher level interface to the edit and
* create key operations in PgpKeyOperation. It takes care of fetching
* and saving the key before and after the operation.
@@ -58,8 +63,16 @@ public class EditKeyOperation extends BaseOperation<SaveKeyringParcel> {
super(context, providerHelper, progressable, cancelled);
}
/**
* Saves an edited key, and uploads it to a server atomically or otherwise as
* specified in saveParcel
*
* @param saveParcel primary input to the operation
* @param cryptoInput input that changes if user interaction is required
* @return the result of the operation
*/
@NonNull
public OperationResult execute(SaveKeyringParcel saveParcel, CryptoInputParcel cryptoInput) {
public InputPendingResult execute(SaveKeyringParcel saveParcel, CryptoInputParcel cryptoInput) {
OperationLog log = new OperationLog();
log.add(LogType.MSG_ED, 0);
@@ -120,6 +133,36 @@ public class EditKeyOperation extends BaseOperation<SaveKeyringParcel> {
// It's a success, so this must be non-null now
UncachedKeyRing ring = modifyResult.getRing();
if (saveParcel.isUpload()) {
UncachedKeyRing publicKeyRing;
try {
publicKeyRing = ring.extractPublicKeyRing();
} catch (IOException e) {
log.add(LogType.MSG_ED_ERROR_EXTRACTING_PUBLIC_UPLOAD, 1);
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
ExportKeyringParcel exportKeyringParcel =
new ExportKeyringParcel(saveParcel.getUploadKeyserver(),
publicKeyRing);
ExportResult uploadResult =
new ExportOperation(mContext, mProviderHelper, mProgressable)
.execute(exportKeyringParcel, cryptoInput);
if (uploadResult.isPending()) {
return uploadResult;
} else if (!uploadResult.success() && saveParcel.isUploadAtomic()) {
// if atomic, update fail implies edit operation should also fail and not save
log.add(uploadResult, 2);
return new EditKeyResult(log, RequiredInputParcel.createRetryUploadOperation(),
cryptoInput);
} else {
// upload succeeded or not atomic so we continue
log.add(uploadResult, 2);
}
}
// Save the new keyring.
SaveKeyringResult saveResult = mProviderHelper
.saveSecretKeyRing(ring, new ProgressScaler(mProgressable, 60, 95, 100));
@@ -131,7 +174,7 @@ public class EditKeyOperation extends BaseOperation<SaveKeyringParcel> {
}
// There is a new passphrase - cache it
if (saveParcel.mNewUnlock != null) {
if (saveParcel.mNewUnlock != null && cryptoInput.mCachePassphrase) {
log.add(LogType.MSG_ED_CACHING_NEW, 1);
// NOTE: Don't cache empty passphrases! Important for MOVE_KEY_TO_CARD
@@ -160,5 +203,4 @@ public class EditKeyOperation extends BaseOperation<SaveKeyringParcel> {
return new EditKeyResult(EditKeyResult.RESULT_OK, log, ring.getMasterKeyId());
}
}

View File

@@ -18,7 +18,6 @@
package org.sufficientlysecure.keychain.operations;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
@@ -89,6 +88,8 @@ public class ExportOperation extends BaseOperation<ExportKeyringParcel> {
}
public ExportResult uploadKeyRingToServer(HkpKeyserver server, UncachedKeyRing keyring, Proxy proxy) {
mProgressable.setProgress(R.string.progress_uploading, 0, 1);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ArmoredOutputStream aos = null;
OperationLog log = new OperationLog();
@@ -117,6 +118,7 @@ public class ExportOperation extends BaseOperation<ExportKeyringParcel> {
log.add(LogType.MSG_EXPORT_ERROR_UPLOAD, 1);
return new ExportResult(ExportResult.RESULT_ERROR, log);
} finally {
mProgressable.setProgress(R.string.progress_uploading, 1, 1);
try {
if (aos != null) {
aos.close();
@@ -341,7 +343,7 @@ public class ExportOperation extends BaseOperation<ExportKeyringParcel> {
// explicit proxy not set
if (!OrbotHelper.isOrbotInRequiredState(mContext)) {
return new ExportResult(null,
RequiredInputParcel.createOrbotRequiredOperation());
RequiredInputParcel.createOrbotRequiredOperation(), cryptoInput);
}
proxy = Preferences.getPreferences(mContext).getProxyPrefs()
.parcelableProxy.getProxy();
@@ -351,10 +353,15 @@ public class ExportOperation extends BaseOperation<ExportKeyringParcel> {
HkpKeyserver hkpKeyserver = new HkpKeyserver(exportInput.mKeyserver);
try {
CanonicalizedPublicKeyRing keyring
= mProviderHelper.getCanonicalizedPublicKeyRing(
exportInput.mCanonicalizedPublicKeyringUri);
return uploadKeyRingToServer(hkpKeyserver, keyring, proxy);
if (exportInput.mCanonicalizedPublicKeyringUri != null) {
CanonicalizedPublicKeyRing keyring
= mProviderHelper.getCanonicalizedPublicKeyRing(
exportInput.mCanonicalizedPublicKeyringUri);
return uploadKeyRingToServer(hkpKeyserver, keyring, proxy);
} else {
return uploadKeyRingToServer(hkpKeyserver, exportInput.mUncachedKeyRing,
proxy);
}
} catch (ProviderHelper.NotFoundException e) {
Log.e(Constants.TAG, "error uploading key", e);
return new ExportResult(ExportResult.RESULT_ERROR, new OperationLog());

View File

@@ -404,7 +404,7 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> {
if(!OrbotHelper.isOrbotInRequiredState(mContext)) {
// show dialog to enable/install dialog
return new ImportKeyResult(null,
RequiredInputParcel.createOrbotRequiredOperation());
RequiredInputParcel.createOrbotRequiredOperation(), cryptoInput);
}
proxy = Preferences.getPreferences(mContext).getProxyPrefs().parcelableProxy
.getProxy();

View File

@@ -69,7 +69,7 @@ public class KeybaseVerificationOperation extends BaseOperation<KeybaseVerificat
// explicit proxy not set
if (!OrbotHelper.isOrbotInRequiredState(mContext)) {
return new KeybaseVerificationResult(null,
RequiredInputParcel.createOrbotRequiredOperation());
RequiredInputParcel.createOrbotRequiredOperation(), cryptoInput);
}
proxy = Preferences.getPreferences(mContext).getProxyPrefs()
.parcelableProxy.getProxy();

View File

@@ -0,0 +1,145 @@
/*
* Copyright (C) 2013-2015 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
* Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
*
* 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.operations;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.support.annotation.NonNull;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.operations.results.InputPendingResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.operations.results.RevokeResult;
import org.sufficientlysecure.keychain.pgp.Progressable;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.RevokeKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.Log;
public class RevokeOperation extends BaseOperation<RevokeKeyringParcel> {
public RevokeOperation(Context context, ProviderHelper providerHelper, Progressable progressable) {
super(context, providerHelper, progressable);
}
@NonNull
@Override
public OperationResult execute(RevokeKeyringParcel revokeKeyringParcel,
CryptoInputParcel cryptoInputParcel) {
// we don't cache passphrases during revocation
cryptoInputParcel.mCachePassphrase = false;
long masterKeyId = revokeKeyringParcel.mMasterKeyId;
OperationResult.OperationLog log = new OperationResult.OperationLog();
log.add(OperationResult.LogType.MSG_REVOKE, 0,
KeyFormattingUtils.beautifyKeyId(masterKeyId));
try {
Uri secretUri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(masterKeyId);
CachedPublicKeyRing keyRing = mProviderHelper.getCachedPublicKeyRing(secretUri);
// check if this is a master secret key we can work with
switch (keyRing.getSecretKeyType(masterKeyId)) {
case GNU_DUMMY:
log.add(OperationResult.LogType.MSG_EK_ERROR_DUMMY, 1);
return new RevokeResult(RevokeResult.RESULT_ERROR, log, masterKeyId);
}
SaveKeyringParcel saveKeyringParcel = getRevokedSaveKeyringParcel(masterKeyId,
keyRing.getFingerprint());
// all revoke operations are made atomic as of now
saveKeyringParcel.setUpdateOptions(revokeKeyringParcel.mUpload, true,
revokeKeyringParcel.mKeyserver);
InputPendingResult revokeAndUploadResult = new EditKeyOperation(mContext,
mProviderHelper, mProgressable, mCancelled)
.execute(saveKeyringParcel, cryptoInputParcel);
if (revokeAndUploadResult.isPending()) {
return revokeAndUploadResult;
}
log.add(revokeAndUploadResult, 1);
if (revokeAndUploadResult.success()) {
log.add(OperationResult.LogType.MSG_REVOKE_OK, 1);
return new RevokeResult(RevokeResult.RESULT_OK, log, masterKeyId);
} else {
log.add(OperationResult.LogType.MSG_REVOKE_KEY_FAIL, 1);
return new RevokeResult(RevokeResult.RESULT_ERROR, log, masterKeyId);
}
} catch (PgpKeyNotFoundException | ProviderHelper.NotFoundException e) {
Log.e(Constants.TAG, "could not find key to revoke", e);
log.add(OperationResult.LogType.MSG_REVOKE_KEY_FAIL, 1);
return new RevokeResult(RevokeResult.RESULT_ERROR, log, masterKeyId);
}
}
private SaveKeyringParcel getRevokedSaveKeyringParcel(long masterKeyId, byte[] fingerprint) {
final String[] SUBKEYS_PROJECTION = new String[]{
KeychainContract.Keys.KEY_ID
};
final int INDEX_KEY_ID = 0;
Uri keysUri = KeychainContract.Keys.buildKeysUri(masterKeyId);
Cursor subKeyCursor =
mContext.getContentResolver().query(keysUri, SUBKEYS_PROJECTION, null, null, null);
SaveKeyringParcel saveKeyringParcel =
new SaveKeyringParcel(masterKeyId, fingerprint);
// add all subkeys, for revocation
while (subKeyCursor != null && subKeyCursor.moveToNext()) {
saveKeyringParcel.mRevokeSubKeys.add(subKeyCursor.getLong(INDEX_KEY_ID));
}
if (subKeyCursor != null) {
subKeyCursor.close();
}
final String[] USER_IDS_PROJECTION = new String[]{
KeychainContract.UserPackets.USER_ID
};
final int INDEX_USER_ID = 0;
Uri userIdsUri = KeychainContract.UserPackets.buildUserIdsUri(masterKeyId);
Cursor userIdCursor = mContext.getContentResolver().query(
userIdsUri, USER_IDS_PROJECTION, null, null, null);
while (userIdCursor != null && userIdCursor.moveToNext()) {
saveKeyringParcel.mRevokeUserIds.add(userIdCursor.getString(INDEX_USER_ID));
}
if (userIdCursor != null) {
userIdCursor.close();
}
return saveKeyringParcel;
}
}

View File

@@ -155,7 +155,7 @@ public class SignEncryptOperation extends BaseOperation<SignEncryptParcel> {
RequiredInputParcel requiredInput = result.getRequiredInputParcel();
// Passphrase returns immediately, nfc are aggregated
if (requiredInput.mType == RequiredInputType.PASSPHRASE) {
return new SignEncryptResult(log, requiredInput, results);
return new SignEncryptResult(log, requiredInput, results, cryptoInput);
}
if (pendingInputBuilder == null) {
pendingInputBuilder = new NfcSignOperationsBuilder(requiredInput.mSignatureTime,
@@ -173,7 +173,7 @@ public class SignEncryptOperation extends BaseOperation<SignEncryptParcel> {
} while (!inputUris.isEmpty());
if (pendingInputBuilder != null && !pendingInputBuilder.isEmpty()) {
return new SignEncryptResult(log, pendingInputBuilder.build(), results);
return new SignEncryptResult(log, pendingInputBuilder.build(), results, cryptoInput);
}
if (!outputUris.isEmpty()) {

View File

@@ -23,6 +23,7 @@ import android.content.Intent;
import android.os.Parcel;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
import org.sufficientlysecure.keychain.ui.LogDisplayActivity;
import org.sufficientlysecure.keychain.ui.LogDisplayFragment;
@@ -38,8 +39,9 @@ public class CertifyResult extends InputPendingResult {
super(result, log);
}
public CertifyResult(OperationLog log, RequiredInputParcel requiredInput) {
super(log, requiredInput);
public CertifyResult(OperationLog log, RequiredInputParcel requiredInput,
CryptoInputParcel cryptoInputParcel) {
super(log, requiredInput, cryptoInputParcel);
}
public CertifyResult(int result, OperationLog log, int certifyOk, int certifyError, int uploadOk, int uploadError) {

View File

@@ -24,7 +24,6 @@ import org.openintents.openpgp.OpenPgpMetadata;
import org.openintents.openpgp.OpenPgpSignatureResult;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
import org.sufficientlysecure.keychain.util.Passphrase;
public class DecryptVerifyResult extends InputPendingResult {
@@ -45,8 +44,9 @@ public class DecryptVerifyResult extends InputPendingResult {
super(result, log);
}
public DecryptVerifyResult(OperationLog log, RequiredInputParcel requiredInput) {
super(log, requiredInput);
public DecryptVerifyResult(OperationLog log, RequiredInputParcel requiredInput,
CryptoInputParcel cryptoInputParcel) {
super(log, requiredInput, cryptoInputParcel);
}
public DecryptVerifyResult(Parcel source) {

View File

@@ -21,8 +21,11 @@ package org.sufficientlysecure.keychain.operations.results;
import android.app.Activity;
import android.content.Intent;
import android.os.Parcel;
import android.support.annotation.Nullable;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
import org.sufficientlysecure.keychain.ui.LogDisplayActivity;
import org.sufficientlysecure.keychain.ui.LogDisplayFragment;
import org.sufficientlysecure.keychain.ui.util.Notify;
@@ -30,7 +33,7 @@ import org.sufficientlysecure.keychain.ui.util.Notify.ActionListener;
import org.sufficientlysecure.keychain.ui.util.Notify.Showable;
import org.sufficientlysecure.keychain.ui.util.Notify.Style;
public class DeleteResult extends OperationResult {
public class DeleteResult extends InputPendingResult {
final public int mOk, mFail;
@@ -40,6 +43,19 @@ public class DeleteResult extends OperationResult {
mFail = fail;
}
/**
* used when more input is required
* @param log operation log upto point of required input, if any
* @param requiredInput represents input required
*/
public DeleteResult(@Nullable OperationLog log, RequiredInputParcel requiredInput,
CryptoInputParcel cryptoInputParcel) {
super(log, requiredInput, cryptoInputParcel);
// values are not to be used
mOk = -1;
mFail = -1;
}
/** Construct from a parcel - trivial because we have no extra data. */
public DeleteResult(Parcel source) {
super(source);
@@ -109,7 +125,10 @@ public class DeleteResult extends OperationResult {
} else {
duration = 0;
style = Style.ERROR;
if (mFail == 0) {
if (mLog.getLast().mType == LogType.MSG_DEL_ERROR_MULTI_SECRET) {
str = activity.getString(R.string.secret_cannot_multiple);
}
else if (mFail == 0) {
str = activity.getString(R.string.delete_nothing);
} else {
str = activity.getResources().getQuantityString(R.plurals.delete_fail, mFail);

View File

@@ -20,7 +20,10 @@ package org.sufficientlysecure.keychain.operations.results;
import android.os.Parcel;
public class EditKeyResult extends OperationResult {
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
public class EditKeyResult extends InputPendingResult {
public final Long mMasterKeyId;
@@ -29,6 +32,12 @@ public class EditKeyResult extends OperationResult {
mMasterKeyId = masterKeyId;
}
public EditKeyResult(OperationLog log, RequiredInputParcel requiredInput,
CryptoInputParcel cryptoInputParcel) {
super(log, requiredInput, cryptoInputParcel);
mMasterKeyId = null;
}
public EditKeyResult(Parcel source) {
super(source);
mMasterKeyId = source.readInt() != 0 ? source.readLong() : null;

View File

@@ -19,6 +19,7 @@ package org.sufficientlysecure.keychain.operations.results;
import android.os.Parcel;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
public class ExportResult extends InputPendingResult {
@@ -36,8 +37,9 @@ public class ExportResult extends InputPendingResult {
}
public ExportResult(OperationLog log, RequiredInputParcel requiredInputParcel) {
super(log, requiredInputParcel);
public ExportResult(OperationLog log, RequiredInputParcel requiredInputParcel,
CryptoInputParcel cryptoInputParcel) {
super(log, requiredInputParcel, cryptoInputParcel);
// we won't use these values
mOkPublic = -1;
mOkSecret = -1;

View File

@@ -20,6 +20,7 @@ package org.sufficientlysecure.keychain.operations.results;
import android.os.Parcel;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
public class GetKeyResult extends InputPendingResult {
@@ -38,8 +39,9 @@ public class GetKeyResult extends InputPendingResult {
super(result, log);
}
public GetKeyResult(OperationLog log, RequiredInputParcel requiredInput) {
super(log, requiredInput);
public GetKeyResult(OperationLog log, RequiredInputParcel requiredInput,
CryptoInputParcel cryptoInputParcel) {
super(log, requiredInput, cryptoInputParcel);
}
public static final int RESULT_ERROR_NO_VALID_KEYS = RESULT_ERROR + 8;

View File

@@ -96,8 +96,9 @@ public class ImportKeyResult extends InputPendingResult {
mImportedMasterKeyIds = importedMasterKeyIds;
}
public ImportKeyResult(OperationLog log, RequiredInputParcel requiredInputParcel) {
super(log, requiredInputParcel);
public ImportKeyResult(OperationLog log, RequiredInputParcel requiredInputParcel,
CryptoInputParcel cryptoInputParcel) {
super(log, requiredInputParcel, cryptoInputParcel);
// just assign default values, we won't use them anyway
mNewKeys = 0;
mUpdatedKeys = 0;

View File

@@ -18,10 +18,9 @@
package org.sufficientlysecure.keychain.operations.results;
import java.util.ArrayList;
import android.os.Parcel;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
public class InputPendingResult extends OperationResult {
@@ -30,26 +29,33 @@ public class InputPendingResult extends OperationResult {
public static final int RESULT_PENDING = RESULT_ERROR + 8;
final RequiredInputParcel mRequiredInput;
// in case operation needs to add to/changes the cryptoInputParcel sent to it
public final CryptoInputParcel mCryptoInputParcel;
public InputPendingResult(int result, OperationLog log) {
super(result, log);
mRequiredInput = null;
mCryptoInputParcel = null;
}
public InputPendingResult(OperationLog log, RequiredInputParcel requiredInput) {
public InputPendingResult(OperationLog log, RequiredInputParcel requiredInput,
CryptoInputParcel cryptoInputParcel) {
super(RESULT_PENDING, log);
mRequiredInput = requiredInput;
mCryptoInputParcel = cryptoInputParcel;
}
public InputPendingResult(Parcel source) {
super(source);
mRequiredInput = source.readParcelable(getClass().getClassLoader());
mCryptoInputParcel = source.readParcelable(getClass().getClassLoader());
}
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeParcelable(mRequiredInput, 0);
dest.writeParcelable(mCryptoInputParcel, 0);
}
public boolean isPending() {

View File

@@ -24,6 +24,7 @@ import android.os.Parcelable;
import com.textuality.keybase.lib.KeybaseException;
import com.textuality.keybase.lib.prover.Prover;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
public class KeybaseVerificationResult extends InputPendingResult {
@@ -46,8 +47,9 @@ public class KeybaseVerificationResult extends InputPendingResult {
mPresenceLabel = prover.getPresenceLabel();
}
public KeybaseVerificationResult(OperationLog log, RequiredInputParcel requiredInputParcel) {
super(log, requiredInputParcel);
public KeybaseVerificationResult(OperationLog log, RequiredInputParcel requiredInputParcel,
CryptoInputParcel cryptoInputParcel) {
super(log, requiredInputParcel, cryptoInputParcel);
mProofUrl = null;
mPresenceUrl = null;
mPresenceLabel = null;

View File

@@ -571,6 +571,8 @@ public abstract class OperationResult implements Parcelable {
MSG_ED_CACHING_NEW (LogLevel.DEBUG, R.string.msg_ed_caching_new),
MSG_ED_ERROR_NO_PARCEL (LogLevel.ERROR, R.string.msg_ed_error_no_parcel),
MSG_ED_ERROR_KEY_NOT_FOUND (LogLevel.ERROR, R.string.msg_ed_error_key_not_found),
MSG_ED_ERROR_EXTRACTING_PUBLIC_UPLOAD (LogLevel.ERROR,
R.string.msg_ed_error_extract_public_upload),
MSG_ED_FETCHING (LogLevel.DEBUG, R.string.msg_ed_fetching),
MSG_ED_SUCCESS (LogLevel.OK, R.string.msg_ed_success),
@@ -752,7 +754,7 @@ public abstract class OperationResult implements Parcelable {
MSG_GET_QUERY_FAILED(LogLevel.ERROR, R.string.msg_download_query_failed),
MSG_DEL_ERROR_EMPTY (LogLevel.ERROR, R.string.msg_del_error_empty),
MSG_DEL_ERROR_MULTI_SECRET (LogLevel.DEBUG, R.string.msg_del_error_multi_secret),
MSG_DEL_ERROR_MULTI_SECRET (LogLevel.ERROR, R.string.msg_del_error_multi_secret),
MSG_DEL (LogLevel.START, R.plurals.msg_del),
MSG_DEL_KEY (LogLevel.DEBUG, R.string.msg_del_key),
MSG_DEL_KEY_FAIL (LogLevel.WARN, R.string.msg_del_key_fail),
@@ -760,6 +762,13 @@ public abstract class OperationResult implements Parcelable {
MSG_DEL_OK (LogLevel.OK, R.plurals.msg_del_ok),
MSG_DEL_FAIL (LogLevel.WARN, R.plurals.msg_del_fail),
MSG_REVOKE_ERROR_EMPTY (LogLevel.ERROR, R.string.msg_revoke_error_empty),
MSG_REVOKE_ERROR_MULTI_SECRET (LogLevel.DEBUG, R.string.msg_revoke_error_multi_secret),
MSG_REVOKE_ERROR_NOT_FOUND (LogLevel.DEBUG, R.string.msg_revoke_error_multi_secret),
MSG_REVOKE (LogLevel.DEBUG, R.string.msg_revoke_key),
MSG_REVOKE_KEY_FAIL (LogLevel.ERROR, R.string.msg_revoke_key_fail),
MSG_REVOKE_OK (LogLevel.OK, R.string.msg_revoke_ok),
// keybase verification
MSG_KEYBASE_VERIFICATION(LogLevel.START, R.string.msg_keybase_verification),

View File

@@ -22,6 +22,7 @@ import android.os.Parcel;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
@@ -37,8 +38,9 @@ public class PgpEditKeyResult extends InputPendingResult {
mRingMasterKeyId = ring != null ? ring.getMasterKeyId() : Constants.key.none;
}
public PgpEditKeyResult(OperationLog log, RequiredInputParcel requiredInput) {
super(log, requiredInput);
public PgpEditKeyResult(OperationLog log, RequiredInputParcel requiredInput,
CryptoInputParcel cryptoInputParcel) {
super(log, requiredInput, cryptoInputParcel);
mRingMasterKeyId = Constants.key.none;
}

View File

@@ -19,6 +19,7 @@ package org.sufficientlysecure.keychain.operations.results;
import android.os.Parcel;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
@@ -38,8 +39,9 @@ public class PgpSignEncryptResult extends InputPendingResult {
super(result, log);
}
public PgpSignEncryptResult(OperationLog log, RequiredInputParcel requiredInput) {
super(log, requiredInput);
public PgpSignEncryptResult(OperationLog log, RequiredInputParcel requiredInput,
CryptoInputParcel cryptoInputParcel) {
super(log, requiredInput, cryptoInputParcel);
}
public PgpSignEncryptResult(Parcel source) {

View File

@@ -0,0 +1,123 @@
/*
* Copyright (C) 2013-2015 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
* Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
*
* 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.operations.results;
import android.app.Activity;
import android.content.Intent;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.Nullable;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
import org.sufficientlysecure.keychain.ui.LogDisplayActivity;
import org.sufficientlysecure.keychain.ui.LogDisplayFragment;
import org.sufficientlysecure.keychain.ui.util.Notify;
public class RevokeResult extends InputPendingResult {
public final long mMasterKeyId;
public RevokeResult(int result, OperationLog log, long masterKeyId) {
super(result, log);
mMasterKeyId = masterKeyId;
}
/**
* used when more input is required
*
* @param log operation log upto point of required input, if any
* @param requiredInput represents input required
*/
@SuppressWarnings("unused") // standard pattern across all results, we might need it later
public RevokeResult(@Nullable OperationLog log, RequiredInputParcel requiredInput,
CryptoInputParcel cryptoInputParcel) {
super(log, requiredInput, cryptoInputParcel);
// we won't use these values
mMasterKeyId = -1;
}
/** Construct from a parcel - trivial because we have no extra data. */
public RevokeResult(Parcel source) {
super(source);
mMasterKeyId = source.readLong();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeLong(mMasterKeyId);
}
public static final Parcelable.Creator<RevokeResult> CREATOR = new Parcelable.Creator<RevokeResult>() {
@Override
public RevokeResult createFromParcel(Parcel in) {
return new RevokeResult(in);
}
@Override
public RevokeResult[] newArray(int size) {
return new RevokeResult[size];
}
};
@Override
public Notify.Showable createNotify(final Activity activity) {
int resultType = getResult();
String str;
int duration;
Notify.Style style;
// Not an overall failure
if ((resultType & OperationResult.RESULT_ERROR) == 0) {
duration = Notify.LENGTH_LONG;
// New and updated keys
if (resultType == OperationResult.RESULT_OK) {
style = Notify.Style.OK;
str = activity.getString(R.string.revoke_ok);
} else {
duration = 0;
style = Notify.Style.ERROR;
str = "internal error";
}
} else {
duration = 0;
style = Notify.Style.ERROR;
str = activity.getString(R.string.revoke_fail);
}
return Notify.create(activity, str, duration, style, new Notify.ActionListener() {
@Override
public void onAction() {
Intent intent = new Intent(
activity, LogDisplayActivity.class);
intent.putExtra(LogDisplayFragment.EXTRA_RESULT, RevokeResult.this);
activity.startActivity(intent);
}
}, R.string.snackbar_details);
}
}

View File

@@ -19,18 +19,20 @@ package org.sufficientlysecure.keychain.operations.results;
import android.os.Parcel;
import java.util.ArrayList;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
import java.util.ArrayList;
public class SignEncryptResult extends InputPendingResult {
ArrayList<PgpSignEncryptResult> mResults;
byte[] mResultBytes;
public SignEncryptResult(OperationLog log, RequiredInputParcel requiredInput, ArrayList<PgpSignEncryptResult> results) {
super(log, requiredInput);
public SignEncryptResult(OperationLog log, RequiredInputParcel requiredInput,
ArrayList<PgpSignEncryptResult> results,
CryptoInputParcel cryptoInputParcel) {
super(log, requiredInput, cryptoInputParcel);
mResults = results;
}

View File

@@ -459,7 +459,8 @@ public class PgpDecryptVerify extends BaseOperation<PgpDecryptVerifyInputParcel>
log.add(LogType.MSG_DC_PENDING_PASSPHRASE, indent + 1);
return new DecryptVerifyResult(log,
RequiredInputParcel.createRequiredDecryptPassphrase(
secretKeyRing.getMasterKeyId(), secretEncryptionKey.getKeyId()));
secretKeyRing.getMasterKeyId(), secretEncryptionKey.getKeyId()),
cryptoInput);
}
}
@@ -498,7 +499,8 @@ public class PgpDecryptVerify extends BaseOperation<PgpDecryptVerifyInputParcel>
if (passphrase == null) {
log.add(LogType.MSG_DC_PENDING_PASSPHRASE, indent + 1);
return new DecryptVerifyResult(log,
RequiredInputParcel.createRequiredSymmetricPassphrase());
RequiredInputParcel.createRequiredSymmetricPassphrase(),
cryptoInput);
}
} else {
@@ -544,7 +546,7 @@ public class PgpDecryptVerify extends BaseOperation<PgpDecryptVerifyInputParcel>
} catch (PGPDataValidationException e) {
log.add(LogType.MSG_DC_ERROR_SYM_PASSPHRASE, indent +1);
return new DecryptVerifyResult(log,
RequiredInputParcel.createRequiredSymmetricPassphrase());
RequiredInputParcel.createRequiredSymmetricPassphrase(), cryptoInput);
}
encryptedData = encryptedDataSymmetric;
@@ -580,7 +582,8 @@ public class PgpDecryptVerify extends BaseOperation<PgpDecryptVerifyInputParcel>
return new DecryptVerifyResult(log, RequiredInputParcel.createNfcDecryptOperation(
secretEncryptionKey.getRing().getMasterKeyId(),
secretEncryptionKey.getKeyId(), encryptedDataAsymmetric.getSessionKey()[0]
));
),
cryptoInput);
}

View File

@@ -22,6 +22,7 @@ import org.spongycastle.bcpg.PublicKeyAlgorithmTags;
import org.spongycastle.bcpg.S2K;
import org.spongycastle.bcpg.sig.Features;
import org.spongycastle.bcpg.sig.KeyFlags;
import org.spongycastle.bcpg.sig.RevocationReasonTags;
import org.spongycastle.jce.spec.ElGamalParameterSpec;
import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPKeyFlags;
@@ -469,7 +470,7 @@ public class PgpKeyOperation {
log.add(LogType.MSG_MF_REQUIRE_PASSPHRASE, indent);
return new PgpEditKeyResult(log, RequiredInputParcel.createRequiredSignPassphrase(
masterSecretKey.getKeyID(), masterSecretKey.getKeyID(),
cryptoInput.getSignatureTime()));
cryptoInput.getSignatureTime()), cryptoInput);
}
// read masterKeyFlags, and use the same as before.
@@ -1105,12 +1106,12 @@ public class PgpKeyOperation {
if (!nfcSignOps.isEmpty()) {
log.add(LogType.MSG_MF_REQUIRE_DIVERT, indent);
return new PgpEditKeyResult(log, nfcSignOps.build());
return new PgpEditKeyResult(log, nfcSignOps.build(), cryptoInput);
}
if (!nfcKeyToCardOps.isEmpty()) {
log.add(LogType.MSG_MF_REQUIRE_DIVERT, indent);
return new PgpEditKeyResult(log, nfcKeyToCardOps.build());
return new PgpEditKeyResult(log, nfcKeyToCardOps.build(), cryptoInput);
}
log.add(LogType.MSG_MF_SUCCESS, indent);
@@ -1525,6 +1526,9 @@ public class PgpKeyOperation {
throws IOException, PGPException, SignatureException {
PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
// we use the tag NO_REASON since gnupg does not care about the tag while verifying
// signatures with a revoked key, the warning is the same
subHashedPacketsGen.setRevocationReason(true, RevocationReasonTags.NO_REASON, "");
subHashedPacketsGen.setSignatureCreationTime(true, creationTime);
sGen.setHashedSubpackets(subHashedPacketsGen.generate());
sGen.init(PGPSignature.CERTIFICATION_REVOCATION, masterPrivateKey);
@@ -1537,6 +1541,9 @@ public class PgpKeyOperation {
throws IOException, PGPException, SignatureException {
PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
// we use the tag NO_REASON since gnupg does not care about the tag while verifying
// signatures with a revoked key, the warning is the same
subHashedPacketsGen.setRevocationReason(true, RevocationReasonTags.NO_REASON, "");
subHashedPacketsGen.setSignatureCreationTime(true, creationTime);
sGen.setHashedSubpackets(subHashedPacketsGen.generate());
// Generate key revocation or subkey revocation, depending on master/subkey-ness

View File

@@ -43,7 +43,6 @@ import org.sufficientlysecure.keychain.operations.results.OperationResult.LogTyp
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
import org.sufficientlysecure.keychain.operations.results.PgpSignEncryptResult;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
@@ -200,7 +199,7 @@ public class PgpSignEncryptOperation extends BaseOperation {
log.add(LogType.MSG_PSE_PENDING_PASSPHRASE, indent + 1);
return new PgpSignEncryptResult(log, RequiredInputParcel.createRequiredSignPassphrase(
signingKeyRing.getMasterKeyId(), signingKey.getKeyId(),
cryptoInput.getSignatureTime()));
cryptoInput.getSignatureTime()), cryptoInput);
}
if (!signingKey.unlock(localPassphrase)) {
log.add(LogType.MSG_PSE_ERROR_BAD_PASSPHRASE, indent);
@@ -513,7 +512,7 @@ public class PgpSignEncryptOperation extends BaseOperation {
log.add(LogType.MSG_PSE_PENDING_NFC, indent);
return new PgpSignEncryptResult(log, RequiredInputParcel.createNfcSignOperation(
signingKey.getRing().getMasterKeyId(), signingKey.getKeyId(),
e.hashToSign, e.hashAlgo, cryptoInput.getSignatureTime()));
e.hashToSign, e.hashAlgo, cryptoInput.getSignatureTime()), cryptoInput);
}
}

View File

@@ -48,6 +48,7 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
@@ -78,7 +79,7 @@ import java.util.TreeSet;
*
*/
@SuppressWarnings("unchecked")
public class UncachedKeyRing {
public class UncachedKeyRing implements Serializable {
final PGPKeyRing mRing;
final boolean mIsSecret;

View File

@@ -64,6 +64,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
public class OpenPgpService extends RemoteService {
@@ -164,7 +165,9 @@ public class OpenPgpService extends RemoteService {
}
private static PendingIntent getRequiredInputPendingIntent(Context context,
Intent data, RequiredInputParcel requiredInput) {
Intent data,
RequiredInputParcel requiredInput,
CryptoInputParcel cryptoInput) {
switch (requiredInput.mType) {
case NFC_MOVE_KEY_TO_CARD:
@@ -175,6 +178,7 @@ public class OpenPgpService extends RemoteService {
// pass params through to activity that it can be returned again later to repeat pgp operation
intent.putExtra(NfcOperationActivity.EXTRA_SERVICE_INTENT, data);
intent.putExtra(NfcOperationActivity.EXTRA_REQUIRED_INPUT, requiredInput);
intent.putExtra(NfcOperationActivity.EXTRA_CRYPTO_INPUT, cryptoInput);
return PendingIntent.getActivity(context, 0, intent,
PendingIntent.FLAG_CANCEL_CURRENT);
}
@@ -185,6 +189,7 @@ public class OpenPgpService extends RemoteService {
// pass params through to activity that it can be returned again later to repeat pgp operation
intent.putExtra(PassphraseDialogActivity.EXTRA_SERVICE_INTENT, data);
intent.putExtra(PassphraseDialogActivity.EXTRA_REQUIRED_INPUT, requiredInput);
intent.putExtra(PassphraseDialogActivity.EXTRA_CRYPTO_INPUT, cryptoInput);
return PendingIntent.getActivity(context, 0, intent,
PendingIntent.FLAG_CANCEL_CURRENT);
}
@@ -279,12 +284,12 @@ public class OpenPgpService extends RemoteService {
CryptoInputParcel inputParcel = CryptoInputParcelCacheService.getCryptoInputParcel(this, data);
if (inputParcel == null) {
inputParcel = new CryptoInputParcel();
inputParcel = new CryptoInputParcel(new Date());
}
// override passphrase in input parcel if given by API call
if (data.hasExtra(OpenPgpApi.EXTRA_PASSPHRASE)) {
inputParcel = new CryptoInputParcel(inputParcel.getSignatureTime(),
new Passphrase(data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE)));
inputParcel.mPassphrase =
new Passphrase(data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE));
}
// execute PGP operation!
@@ -294,7 +299,8 @@ public class OpenPgpService extends RemoteService {
if (pgpResult.isPending()) {
RequiredInputParcel requiredInput = pgpResult.getRequiredInputParcel();
PendingIntent pIntent = getRequiredInputPendingIntent(getBaseContext(), data, requiredInput);
PendingIntent pIntent = getRequiredInputPendingIntent(getBaseContext(), data,
requiredInput, pgpResult.mCryptoInputParcel);
// return PendingIntent to be executed by client
Intent result = new Intent();
@@ -434,12 +440,12 @@ public class OpenPgpService extends RemoteService {
CryptoInputParcel inputParcel = CryptoInputParcelCacheService.getCryptoInputParcel(this, data);
if (inputParcel == null) {
inputParcel = new CryptoInputParcel();
inputParcel = new CryptoInputParcel(new Date());
}
// override passphrase in input parcel if given by API call
if (data.hasExtra(OpenPgpApi.EXTRA_PASSPHRASE)) {
inputParcel = new CryptoInputParcel(inputParcel.getSignatureTime(),
new Passphrase(data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE)));
inputParcel.mPassphrase =
new Passphrase(data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE));
}
PgpSignEncryptOperation op = new PgpSignEncryptOperation(this, new ProviderHelper(getContext()), null);
@@ -449,7 +455,8 @@ public class OpenPgpService extends RemoteService {
if (pgpResult.isPending()) {
RequiredInputParcel requiredInput = pgpResult.getRequiredInputParcel();
PendingIntent pIntent = getRequiredInputPendingIntent(getBaseContext(), data, requiredInput);
PendingIntent pIntent = getRequiredInputPendingIntent(getBaseContext(), data,
requiredInput, pgpResult.mCryptoInputParcel);
// return PendingIntent to be executed by client
Intent result = new Intent();
@@ -519,8 +526,8 @@ public class OpenPgpService extends RemoteService {
}
// override passphrase in input parcel if given by API call
if (data.hasExtra(OpenPgpApi.EXTRA_PASSPHRASE)) {
cryptoInput = new CryptoInputParcel(cryptoInput.getSignatureTime(),
new Passphrase(data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE)));
cryptoInput.mPassphrase =
new Passphrase(data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE));
}
byte[] detachedSignature = data.getByteArrayExtra(OpenPgpApi.EXTRA_DETACHED_SIGNATURE);
@@ -543,7 +550,8 @@ public class OpenPgpService extends RemoteService {
if (pgpResult.isPending()) {
// prepare and return PendingIntent to be executed by client
RequiredInputParcel requiredInput = pgpResult.getRequiredInputParcel();
PendingIntent pIntent = getRequiredInputPendingIntent(getBaseContext(), data, requiredInput);
PendingIntent pIntent = getRequiredInputPendingIntent(getBaseContext(), data,
requiredInput, pgpResult.mCryptoInputParcel);
Intent result = new Intent();
result.putExtra(OpenPgpApi.RESULT_INTENT, pIntent);

View File

@@ -23,9 +23,12 @@ import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
public class ExportKeyringParcel implements Parcelable {
public String mKeyserver;
public Uri mCanonicalizedPublicKeyringUri;
public UncachedKeyRing mUncachedKeyRing;
public boolean mExportSecret;
public long mMasterKeyIds[];
@@ -45,6 +48,12 @@ public class ExportKeyringParcel implements Parcelable {
mCanonicalizedPublicKeyringUri = keyringUri;
}
public ExportKeyringParcel(String keyserver, UncachedKeyRing uncachedKeyRing) {
mExportType = ExportType.UPLOAD_KEYSERVER;
mKeyserver = keyserver;
mUncachedKeyRing = uncachedKeyRing;
}
public ExportKeyringParcel(long[] masterKeyIds, boolean exportSecret, String outputFile) {
mExportType = ExportType.EXPORT_FILE;
mMasterKeyIds = masterKeyIds;
@@ -52,6 +61,7 @@ public class ExportKeyringParcel implements Parcelable {
mOutputFile = outputFile;
}
@SuppressWarnings("unused") // TODO: is it used?
public ExportKeyringParcel(long[] masterKeyIds, boolean exportSecret, Uri outputUri) {
mExportType = ExportType.EXPORT_URI;
mMasterKeyIds = masterKeyIds;
@@ -62,6 +72,7 @@ public class ExportKeyringParcel implements Parcelable {
protected ExportKeyringParcel(Parcel in) {
mKeyserver = in.readString();
mCanonicalizedPublicKeyringUri = (Uri) in.readValue(Uri.class.getClassLoader());
mUncachedKeyRing = (UncachedKeyRing) in.readValue(UncachedKeyRing.class.getClassLoader());
mExportSecret = in.readByte() != 0x00;
mOutputFile = in.readString();
mOutputUri = (Uri) in.readValue(Uri.class.getClassLoader());
@@ -78,6 +89,7 @@ public class ExportKeyringParcel implements Parcelable {
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mKeyserver);
dest.writeValue(mCanonicalizedPublicKeyringUri);
dest.writeValue(mUncachedKeyRing);
dest.writeByte((byte) (mExportSecret ? 0x01 : 0x00));
dest.writeString(mOutputFile);
dest.writeValue(mOutputUri);

View File

@@ -37,6 +37,7 @@ import org.sufficientlysecure.keychain.operations.ExportOperation;
import org.sufficientlysecure.keychain.operations.ImportOperation;
import org.sufficientlysecure.keychain.operations.KeybaseVerificationOperation;
import org.sufficientlysecure.keychain.operations.PromoteKeyOperation;
import org.sufficientlysecure.keychain.operations.RevokeOperation;
import org.sufficientlysecure.keychain.operations.SignEncryptOperation;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
@@ -114,6 +115,8 @@ public class KeychainService extends Service implements Progressable {
} else if (inputParcel instanceof SaveKeyringParcel) {
op = new EditKeyOperation(outerThis, new ProviderHelper(outerThis), outerThis,
mActionCanceled);
} else if (inputParcel instanceof RevokeKeyringParcel) {
op = new RevokeOperation(outerThis, new ProviderHelper(outerThis), outerThis);
} else if (inputParcel instanceof CertifyActionsParcel) {
op = new CertifyOperation(outerThis, new ProviderHelper(outerThis), outerThis,
mActionCanceled);
@@ -135,7 +138,7 @@ public class KeychainService extends Service implements Progressable {
op = new KeybaseVerificationOperation(outerThis, new ProviderHelper(outerThis),
outerThis);
} else {
return;
throw new AssertionError("Unrecognized input parcel in KeychainService!");
}
@SuppressWarnings("unchecked") // this is unchecked, we make sure it's the correct op above!

View File

@@ -0,0 +1,66 @@
/*
* Copyright (C) 2013-2015 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
* Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
*
* 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.service;
import android.os.Parcel;
import android.os.Parcelable;
public class RevokeKeyringParcel implements Parcelable {
final public long mMasterKeyId;
final public boolean mUpload;
final public String mKeyserver;
public RevokeKeyringParcel(long masterKeyId, boolean upload, String keyserver) {
mMasterKeyId = masterKeyId;
mUpload = upload;
mKeyserver = keyserver;
}
protected RevokeKeyringParcel(Parcel in) {
mMasterKeyId = in.readLong();
mUpload = in.readByte() != 0x00;
mKeyserver = in.readString();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(mMasterKeyId);
dest.writeByte((byte) (mUpload ? 0x01 : 0x00));
dest.writeString(mKeyserver);
}
public static final Parcelable.Creator<RevokeKeyringParcel> CREATOR = new Parcelable.Creator<RevokeKeyringParcel>() {
@Override
public RevokeKeyringParcel createFromParcel(Parcel in) {
return new RevokeKeyringParcel(in);
}
@Override
public RevokeKeyringParcel[] newArray(int size) {
return new RevokeKeyringParcel[size];
}
};
}

View File

@@ -65,6 +65,11 @@ public class SaveKeyringParcel implements Parcelable {
public Passphrase mCardPin;
public Passphrase mCardAdminPin;
// private because they have to be set together with setUpdateOptions
private boolean mUpload;
private boolean mUploadAtomic;
private String mKeyserver;
public SaveKeyringParcel() {
reset();
}
@@ -86,6 +91,27 @@ public class SaveKeyringParcel implements Parcelable {
mRevokeSubKeys = new ArrayList<>();
mCardPin = null;
mCardAdminPin = null;
mUpload = false;
mUploadAtomic = false;
mKeyserver = null;
}
public void setUpdateOptions(boolean upload, boolean uploadAtomic, String keysever) {
mUpload = upload;
mUploadAtomic = uploadAtomic;
mKeyserver = keysever;
}
public boolean isUpload() {
return mUpload;
}
public boolean isUploadAtomic() {
return mUploadAtomic;
}
public String getUploadKeyserver() {
return mKeyserver;
}
public boolean isEmpty() {
@@ -216,6 +242,7 @@ public class SaveKeyringParcel implements Parcelable {
}
}
@SuppressWarnings("unchecked") // we verify the reads against writes in writeToParcel
public SaveKeyringParcel(Parcel source) {
mMasterKeyId = source.readInt() != 0 ? source.readLong() : null;
mFingerprint = source.createByteArray();
@@ -234,6 +261,10 @@ public class SaveKeyringParcel implements Parcelable {
mCardPin = source.readParcelable(Passphrase.class.getClassLoader());
mCardAdminPin = source.readParcelable(Passphrase.class.getClassLoader());
mUpload = source.readByte() != 0;
mUploadAtomic = source.readByte() != 0;
mKeyserver = source.readString();
}
@Override
@@ -259,6 +290,10 @@ public class SaveKeyringParcel implements Parcelable {
destination.writeParcelable(mCardPin, flags);
destination.writeParcelable(mCardAdminPin, flags);
destination.writeByte((byte) (mUpload ? 1 : 0));
destination.writeByte((byte) (mUploadAtomic ? 1 : 0));
destination.writeString(mKeyserver);
}
public static final Creator<SaveKeyringParcel> CREATOR = new Creator<SaveKeyringParcel>() {

View File

@@ -33,49 +33,71 @@ import java.util.Map;
*/
public class CryptoInputParcel implements Parcelable {
final Date mSignatureTime;
final Passphrase mPassphrase;
private Date mSignatureTime;
private boolean mHasSignature;
public Passphrase mPassphrase;
// used to supply an explicit proxy to operations that require it
// this is not final so it can be added to an existing CryptoInputParcel
// (e.g) CertifyOperation with upload might require both passphrase and orbot to be enabled
private ParcelableProxy mParcelableProxy;
// specifies whether passphrases should be cached
public boolean mCachePassphrase = true;
// this map contains both decrypted session keys and signed hashes to be
// used in the crypto operation described by this parcel.
private HashMap<ByteBuffer, byte[]> mCryptoData = new HashMap<>();
public CryptoInputParcel() {
mSignatureTime = new Date();
mSignatureTime = null;
mPassphrase = null;
mParcelableProxy = null;
mCachePassphrase = true;
}
public CryptoInputParcel(Date signatureTime, Passphrase passphrase) {
mHasSignature = true;
mSignatureTime = signatureTime == null ? new Date() : signatureTime;
mPassphrase = passphrase;
mParcelableProxy = null;
mCachePassphrase = true;
}
public CryptoInputParcel(Passphrase passphrase) {
mSignatureTime = new Date();
mPassphrase = passphrase;
mParcelableProxy = null;
mCachePassphrase = true;
}
public CryptoInputParcel(Date signatureTime) {
mHasSignature = true;
mSignatureTime = signatureTime == null ? new Date() : signatureTime;
mPassphrase = null;
mParcelableProxy = null;
mCachePassphrase = true;
}
public CryptoInputParcel(ParcelableProxy parcelableProxy) {
mSignatureTime = new Date(); // just for compatibility with parcel-ing
mPassphrase = null;
this();
mParcelableProxy = parcelableProxy;
}
public CryptoInputParcel(Date signatureTime, boolean cachePassphrase) {
mHasSignature = true;
mSignatureTime = signatureTime == null ? new Date() : signatureTime;
mPassphrase = null;
mCachePassphrase = cachePassphrase;
}
public CryptoInputParcel(boolean cachePassphrase) {
mCachePassphrase = cachePassphrase;
}
protected CryptoInputParcel(Parcel source) {
mSignatureTime = new Date(source.readLong());
mHasSignature = source.readByte() != 0;
if (mHasSignature) {
mSignatureTime = new Date(source.readLong());
}
mPassphrase = source.readParcelable(getClass().getClassLoader());
mParcelableProxy = source.readParcelable(getClass().getClassLoader());
mCachePassphrase = source.readByte() != 0;
{
int count = source.readInt();
@@ -96,9 +118,13 @@ public class CryptoInputParcel implements Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(mSignatureTime.getTime());
dest.writeByte((byte) (mHasSignature ? 1 : 0));
if (mHasSignature) {
dest.writeLong(mSignatureTime.getTime());
}
dest.writeParcelable(mPassphrase, 0);
dest.writeParcelable(mParcelableProxy, 0);
dest.writeByte((byte) (mCachePassphrase ? 1 : 0));
dest.writeInt(mCryptoData.size());
for (HashMap.Entry<ByteBuffer, byte[]> entry : mCryptoData.entrySet()) {
@@ -111,6 +137,10 @@ public class CryptoInputParcel implements Parcelable {
mParcelableProxy = parcelableProxy;
}
public void addSignatureTime(Date signatureTime) {
mSignatureTime = signatureTime;
}
public void addCryptoData(byte[] hash, byte[] signedHash) {
mCryptoData.put(ByteBuffer.wrap(hash), signedHash);
}

View File

@@ -3,7 +3,6 @@ package org.sufficientlysecure.keychain.service.input;
import android.os.Parcel;
import android.os.Parcelable;
import org.spongycastle.util.Arrays;
import org.sufficientlysecure.keychain.util.Passphrase;
import java.nio.ByteBuffer;
@@ -15,7 +14,8 @@ import java.util.Date;
public class RequiredInputParcel implements Parcelable {
public enum RequiredInputType {
PASSPHRASE, PASSPHRASE_SYMMETRIC, NFC_SIGN, NFC_DECRYPT, NFC_MOVE_KEY_TO_CARD, ENABLE_ORBOT
PASSPHRASE, PASSPHRASE_SYMMETRIC, NFC_SIGN, NFC_DECRYPT, NFC_MOVE_KEY_TO_CARD, ENABLE_ORBOT,
UPLOAD_FAIL_RETRY
}
public Date mSignatureTime;
@@ -77,6 +77,11 @@ public class RequiredInputParcel implements Parcelable {
return mSubKeyId;
}
public static RequiredInputParcel createRetryUploadOperation() {
return new RequiredInputParcel(RequiredInputType.UPLOAD_FAIL_RETRY,
null, null, null, 0L, 0L);
}
public static RequiredInputParcel createOrbotRequiredOperation() {
return new RequiredInputParcel(RequiredInputType.ENABLE_ORBOT, null, null, null, 0L, 0L);
}

View File

@@ -49,6 +49,7 @@ import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.CertifyActionsParcel;
import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyAction;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.ui.adapter.MultiUserIdsAdapter;
import org.sufficientlysecure.keychain.ui.base.CachingCryptoOperationFragment;
import org.sufficientlysecure.keychain.ui.util.Notify;
@@ -57,6 +58,7 @@ import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Preferences;
import java.util.ArrayList;
import java.util.Date;
public class CertifyKeyFragment
extends CachingCryptoOperationFragment<CertifyActionsParcel, CertifyResult>
@@ -165,7 +167,7 @@ public class CertifyKeyFragment
Notify.create(getActivity(), getString(R.string.select_key_to_certify),
Notify.Style.ERROR).show();
} else {
cryptoOperation();
cryptoOperation(new CryptoInputParcel(new Date()));
}
}
});

View File

@@ -18,6 +18,7 @@
package org.sufficientlysecure.keychain.ui;
import java.util.Date;
import java.util.Iterator;
import android.app.Activity;
@@ -47,6 +48,7 @@ import org.sufficientlysecure.keychain.service.ExportKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import org.sufficientlysecure.keychain.util.Log;
@@ -393,7 +395,7 @@ public class CreateKeyFinalFragment extends Fragment {
mMoveToCardOpHelper = new CryptoOperationHelper<>(2, this, callback, R.string.progress_modify);
mMoveToCardOpHelper.cryptoOperation();
mMoveToCardOpHelper.cryptoOperation(new CryptoInputParcel(new Date()));
}
private void uploadKey(final EditKeyResult saveKeyResult) {

View File

@@ -0,0 +1,430 @@
/*
* Copyright (C) 2013-2015 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
* Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
*
* 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.support.v7.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentActivity;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Spinner;
import android.widget.TextView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.DeleteResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.operations.results.RevokeResult;
import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.DeleteKeyringParcel;
import org.sufficientlysecure.keychain.service.RevokeKeyringParcel;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import org.sufficientlysecure.keychain.ui.dialog.CustomAlertDialogBuilder;
import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.util.Log;
import java.util.Date;
import java.util.HashMap;
public class DeleteKeyDialogActivity extends FragmentActivity {
public static final String EXTRA_DELETE_MASTER_KEY_IDS = "extra_delete_master_key_ids";
public static final String EXTRA_HAS_SECRET = "extra_has_secret";
public static final String EXTRA_KEYSERVER = "extra_keyserver";
private CryptoOperationHelper<DeleteKeyringParcel, DeleteResult> mDeleteOpHelper;
private CryptoOperationHelper<RevokeKeyringParcel, RevokeResult> mRevokeOpHelper;
private long[] mMasterKeyIds;
private boolean mHasSecret;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mDeleteOpHelper = new CryptoOperationHelper<>(1, DeleteKeyDialogActivity.this,
getDeletionCallback(), R.string.progress_deleting);
mRevokeOpHelper = new CryptoOperationHelper<>(2, this,
getRevocationCallback(), R.string.progress_revoking_uploading);
mMasterKeyIds = getIntent().getLongArrayExtra(EXTRA_DELETE_MASTER_KEY_IDS);
mHasSecret = getIntent().getBooleanExtra(EXTRA_HAS_SECRET, false);
if (mMasterKeyIds.length > 1 && mHasSecret) {
// secret keys can only be deleted individually
OperationResult.OperationLog log = new OperationResult.OperationLog();
log.add(OperationResult.LogType.MSG_DEL_ERROR_MULTI_SECRET, 0);
returnResult(new DeleteResult(OperationResult.RESULT_ERROR, log, 0,
mMasterKeyIds.length));
}
if (mMasterKeyIds.length == 1 && mHasSecret) {
// if mMasterKeyIds.length == 0 we let the DeleteOperation respond
try {
HashMap<String, Object> data = new ProviderHelper(this).getUnifiedData(
mMasterKeyIds[0], new String[]{
KeychainContract.KeyRings.USER_ID,
KeychainContract.KeyRings.IS_REVOKED
}, new int[]{
ProviderHelper.FIELD_TYPE_STRING,
ProviderHelper.FIELD_TYPE_INTEGER
}
);
String name;
KeyRing.UserId mainUserId = KeyRing.splitUserId(
(String) data.get(KeychainContract.KeyRings.USER_ID));
if (mainUserId.name != null) {
name = mainUserId.name;
} else {
name = getString(R.string.user_id_no_name);
}
if ((long) data.get(KeychainContract.KeyRings.IS_REVOKED) > 0) {
showNormalDeleteDialog();
} else {
showRevokeDeleteDialog(name);
}
} catch (ProviderHelper.NotFoundException e) {
Log.e(Constants.TAG,
"Secret key to delete not found at DeleteKeyDialogActivity for "
+ mMasterKeyIds[0], e);
finish();
}
} else {
showNormalDeleteDialog();
}
}
private void showNormalDeleteDialog() {
DeleteKeyDialogFragment deleteKeyDialogFragment
= DeleteKeyDialogFragment.newInstance(mMasterKeyIds, mHasSecret);
deleteKeyDialogFragment.show(getSupportFragmentManager(), "deleteKeyDialog");
}
private void showRevokeDeleteDialog(String keyname) {
RevokeDeleteDialogFragment fragment = RevokeDeleteDialogFragment.newInstance(keyname);
fragment.show(getSupportFragmentManager(), "deleteRevokeDialog");
}
private void startRevocationOperation() {
mRevokeOpHelper.cryptoOperation(new CryptoInputParcel(new Date(), false));
}
private void startDeletionOperation() {
mDeleteOpHelper.cryptoOperation();
}
private CryptoOperationHelper.Callback<RevokeKeyringParcel, RevokeResult> getRevocationCallback() {
return new CryptoOperationHelper.Callback<RevokeKeyringParcel, RevokeResult>() {
@Override
public RevokeKeyringParcel createOperationInput() {
return new RevokeKeyringParcel(mMasterKeyIds[0], true,
getIntent().getStringExtra(EXTRA_KEYSERVER));
}
@Override
public void onCryptoOperationSuccess(RevokeResult result) {
returnResult(result);
}
@Override
public void onCryptoOperationCancelled() {
setResult(RESULT_CANCELED);
finish();
}
@Override
public void onCryptoOperationError(RevokeResult result) {
returnResult(result);
}
@Override
public boolean onCryptoSetProgress(String msg, int progress, int max) {
return false;
}
};
}
private CryptoOperationHelper.Callback<DeleteKeyringParcel, DeleteResult> getDeletionCallback() {
return new CryptoOperationHelper.Callback<DeleteKeyringParcel, DeleteResult>() {
@Override
public DeleteKeyringParcel createOperationInput() {
return new DeleteKeyringParcel(mMasterKeyIds, mHasSecret);
}
@Override
public void onCryptoOperationSuccess(DeleteResult result) {
returnResult(result);
}
@Override
public void onCryptoOperationCancelled() {
setResult(RESULT_CANCELED);
finish();
}
@Override
public void onCryptoOperationError(DeleteResult result) {
returnResult(result);
}
@Override
public boolean onCryptoSetProgress(String msg, int progress, int max) {
return false;
}
};
}
private void returnResult(OperationResult result) {
Intent intent = new Intent();
intent.putExtra(OperationResult.EXTRA_RESULT, result);
setResult(RESULT_OK, intent);
finish();
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
mDeleteOpHelper.handleActivityResult(requestCode, resultCode, data);
mRevokeOpHelper.handleActivityResult(requestCode, resultCode, data);
}
public static class DeleteKeyDialogFragment extends DialogFragment {
private static final String ARG_DELETE_MASTER_KEY_IDS = "delete_master_key_ids";
private static final String ARG_HAS_SECRET = "has_secret";
private TextView mMainMessage;
private View mInflateView;
/**
* Creates new instance of this delete file dialog fragment
*/
public static DeleteKeyDialogFragment newInstance(long[] masterKeyIds, boolean hasSecret) {
DeleteKeyDialogFragment frag = new DeleteKeyDialogFragment();
Bundle args = new Bundle();
args.putLongArray(ARG_DELETE_MASTER_KEY_IDS, masterKeyIds);
args.putBoolean(ARG_HAS_SECRET, hasSecret);
frag.setArguments(args);
return frag;
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final FragmentActivity activity = getActivity();
final long[] masterKeyIds = getArguments().getLongArray(ARG_DELETE_MASTER_KEY_IDS);
final boolean hasSecret = getArguments().getBoolean(ARG_HAS_SECRET);
ContextThemeWrapper theme = new ContextThemeWrapper(activity,
R.style.Theme_AppCompat_Light_Dialog);
CustomAlertDialogBuilder builder = new CustomAlertDialogBuilder(theme);
// Setup custom View to display in AlertDialog
LayoutInflater inflater = LayoutInflater.from(theme);
mInflateView = inflater.inflate(R.layout.view_key_delete_fragment, null);
builder.setView(mInflateView);
mMainMessage = (TextView) mInflateView.findViewById(R.id.mainMessage);
// If only a single key has been selected
if (masterKeyIds.length == 1) {
long masterKeyId = masterKeyIds[0];
try {
HashMap<String, Object> data = new ProviderHelper(activity).getUnifiedData(
masterKeyId, new String[]{
KeychainContract.KeyRings.USER_ID,
KeychainContract.KeyRings.HAS_ANY_SECRET
}, new int[]{
ProviderHelper.FIELD_TYPE_STRING,
ProviderHelper.FIELD_TYPE_INTEGER
}
);
String name;
KeyRing.UserId mainUserId = KeyRing.splitUserId((String) data.get(KeychainContract.KeyRings.USER_ID));
if (mainUserId.name != null) {
name = mainUserId.name;
} else {
name = getString(R.string.user_id_no_name);
}
if (hasSecret) {
// show title only for secret key deletions,
// see http://www.google.com/design/spec/components/dialogs.html#dialogs-behavior
builder.setTitle(getString(R.string.title_delete_secret_key, name));
mMainMessage.setText(getString(R.string.secret_key_deletion_confirmation, name));
} else {
mMainMessage.setText(getString(R.string.public_key_deletetion_confirmation, name));
}
} catch (ProviderHelper.NotFoundException e) {
dismiss();
return null;
}
} else {
mMainMessage.setText(R.string.key_deletion_confirmation_multi);
}
builder.setPositiveButton(R.string.btn_delete, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
((DeleteKeyDialogActivity) getActivity()).startDeletionOperation();
}
});
builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
});
return builder.show();
}
@Override
public void onCancel(DialogInterface dialog) {
super.onCancel(dialog);
getActivity().setResult(RESULT_CANCELED);
getActivity().finish();
}
}
public static class RevokeDeleteDialogFragment extends DialogFragment {
public static final String ARG_KEY_NAME = "arg_key_name";
public static RevokeDeleteDialogFragment newInstance(String keyName) {
Bundle args = new Bundle();
args.putString(ARG_KEY_NAME, keyName);
RevokeDeleteDialogFragment frag = new RevokeDeleteDialogFragment();
frag.setArguments(args);
return frag;
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Activity activity = getActivity();
final String CHOICE_REVOKE = getString(R.string.del_rev_dialog_choice_rev_upload);
final String CHOICE_DELETE = getString(R.string.del_rev_dialog_choice_delete);
// if the dialog is displayed from the application class, design is missing
// hack to get holo design (which is not automatically applied due to activity's Theme.NoDisplay
ContextThemeWrapper theme = new ContextThemeWrapper(activity,
R.style.Theme_AppCompat_Light_Dialog);
CustomAlertDialogBuilder builder = new CustomAlertDialogBuilder(theme);
builder.setTitle(getString(R.string.del_rev_dialog_title,
getArguments().get(ARG_KEY_NAME)));
LayoutInflater inflater = LayoutInflater.from(theme);
View view = inflater.inflate(R.layout.del_rev_dialog, null);
builder.setView(view);
final Spinner spinner = (Spinner) view.findViewById(R.id.spinner);
builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
});
builder.setPositiveButton(R.string.del_rev_dialog_btn_revoke,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
String choice = spinner.getSelectedItem().toString();
if (choice.equals(CHOICE_REVOKE)) {
((DeleteKeyDialogActivity) activity)
.startRevocationOperation();
} else if (choice.equals(CHOICE_DELETE)) {
((DeleteKeyDialogActivity) activity)
.showNormalDeleteDialog();
} else {
throw new AssertionError(
"Unsupported delete type in RevokeDeleteDialogFragment");
}
}
});
final AlertDialog alertDialog = builder.show();
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
String choice = parent.getItemAtPosition(pos).toString();
if (choice.equals(CHOICE_REVOKE)) {
alertDialog.getButton(AlertDialog.BUTTON_POSITIVE)
.setText(R.string.del_rev_dialog_btn_revoke);
} else if (choice.equals(CHOICE_DELETE)) {
alertDialog.getButton(AlertDialog.BUTTON_POSITIVE)
.setText(R.string.del_rev_dialog_btn_delete);
} else {
throw new AssertionError(
"Unsupported delete type in RevokeDeleteDialogFragment");
}
}
public void onNothingSelected(AdapterView<?> parent) {
}
});
return alertDialog;
}
@Override
public void onCancel(DialogInterface dialog) {
super.onCancel(dialog);
getActivity().setResult(RESULT_CANCELED);
getActivity().finish();
}
}
}

View File

@@ -68,6 +68,8 @@ import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Passphrase;
import java.util.Date;
public class EditKeyFragment extends QueueingCryptoOperationFragment<SaveKeyringParcel, OperationResult>
implements LoaderManager.LoaderCallbacks<Cursor> {
@@ -151,7 +153,7 @@ public class EditKeyFragment extends QueueingCryptoOperationFragment<SaveKeyring
if (mDataUri == null) {
returnKeyringParcel();
} else {
cryptoOperation(new CryptoInputParcel());
cryptoOperation(new CryptoInputParcel(new Date()));
}
}
}, new OnClickListener() {

View File

@@ -21,6 +21,7 @@ package org.sufficientlysecure.keychain.ui;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -57,6 +58,7 @@ import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.pgp.PgpConstants;
import org.sufficientlysecure.keychain.pgp.SignEncryptParcel;
import org.sufficientlysecure.keychain.provider.TemporaryStorageProvider;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.ui.adapter.SpacesItemDecoration;
import org.sufficientlysecure.keychain.ui.base.CachingCryptoOperationFragment;
import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment;
@@ -279,19 +281,19 @@ public class EncryptFilesFragment
case R.id.encrypt_save: {
hideKeyboard();
mAfterEncryptAction = AfterEncryptAction.SAVE;
cryptoOperation();
cryptoOperation(new CryptoInputParcel(new Date()));
break;
}
case R.id.encrypt_share: {
hideKeyboard();
mAfterEncryptAction = AfterEncryptAction.SHARE;
cryptoOperation();
cryptoOperation(new CryptoInputParcel(new Date()));
break;
}
case R.id.encrypt_copy: {
hideKeyboard();
mAfterEncryptAction = AfterEncryptAction.COPY;
cryptoOperation();
cryptoOperation(new CryptoInputParcel(new Date()));
break;
}
case R.id.check_use_armor: {
@@ -666,7 +668,7 @@ public class EncryptFilesFragment
mOutputUris.add(data.getData());
// make sure this is correct at this point
mAfterEncryptAction = AfterEncryptAction.SAVE;
cryptoOperation();
cryptoOperation(new CryptoInputParcel(new Date()));
}
return;
}

View File

@@ -40,6 +40,7 @@ import org.sufficientlysecure.keychain.operations.results.SignEncryptResult;
import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.pgp.PgpConstants;
import org.sufficientlysecure.keychain.pgp.SignEncryptParcel;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.ui.base.CachingCryptoOperationFragment;
import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.ui.util.Notify.ActionListener;
@@ -48,6 +49,7 @@ import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.Preferences;
import org.sufficientlysecure.keychain.util.ShareHelper;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
@@ -168,13 +170,13 @@ public class EncryptTextFragment
case R.id.encrypt_copy: {
hideKeyboard();
mShareAfterEncrypt = false;
cryptoOperation();
cryptoOperation(new CryptoInputParcel(new Date()));
break;
}
case R.id.encrypt_share: {
hideKeyboard();
mShareAfterEncrypt = true;
cryptoOperation();
cryptoOperation(new CryptoInputParcel(new Date()));
break;
}
default: {

View File

@@ -33,9 +33,6 @@ import android.graphics.Color;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Messenger;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
@@ -60,7 +57,6 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.operations.results.ConsolidateResult;
import org.sufficientlysecure.keychain.operations.results.DeleteResult;
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.provider.KeychainContract;
@@ -71,7 +67,6 @@ import org.sufficientlysecure.keychain.service.ConsolidateInputParcel;
import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
import org.sufficientlysecure.keychain.ui.adapter.KeyAdapter;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.ui.util.Notify.Style;
@@ -91,6 +86,8 @@ public class KeyListFragment extends LoaderFragment
CryptoOperationHelper.Callback<ImportKeyringParcel, ImportKeyResult> {
static final int REQUEST_ACTION = 1;
private static final int REQUEST_DELETE = 2;
private static final int REQUEST_VIEW_KEY = 3;
private KeyListAdapter mAdapter;
private StickyListHeadersListView mStickyList;
@@ -220,7 +217,7 @@ public class KeyListFragment extends LoaderFragment
}
case R.id.menu_key_list_multi_delete: {
ids = mAdapter.getCurrentSelectedMasterKeyIds();
showDeleteKeyDialog(mode, ids, mAdapter.isAnySecretSelected());
showDeleteKeyDialog(ids, mAdapter.isAnySecretSelected());
break;
}
}
@@ -336,7 +333,7 @@ public class KeyListFragment extends LoaderFragment
Intent viewIntent = new Intent(getActivity(), ViewKeyActivity.class);
viewIntent.setData(
KeyRings.buildGenericKeyRingUri(mAdapter.getMasterKeyId(position)));
startActivity(viewIntent);
startActivityForResult(viewIntent, REQUEST_VIEW_KEY);
}
protected void encrypt(ActionMode mode, long[] masterKeyIds) {
@@ -354,38 +351,15 @@ public class KeyListFragment extends LoaderFragment
*
* @param hasSecret must contain whether the list of masterKeyIds contains a secret key or not
*/
public void showDeleteKeyDialog(final ActionMode mode, long[] masterKeyIds, boolean hasSecret) {
// Can only work on singular secret keys
if (hasSecret && masterKeyIds.length > 1) {
Notify.create(getActivity(), R.string.secret_cannot_multiple,
Notify.Style.ERROR).show();
return;
public void showDeleteKeyDialog(long[] masterKeyIds, boolean hasSecret) {
Intent intent = new Intent(getActivity(), DeleteKeyDialogActivity.class);
intent.putExtra(DeleteKeyDialogActivity.EXTRA_DELETE_MASTER_KEY_IDS, masterKeyIds);
intent.putExtra(DeleteKeyDialogActivity.EXTRA_HAS_SECRET, hasSecret);
if (hasSecret) {
intent.putExtra(DeleteKeyDialogActivity.EXTRA_KEYSERVER,
Preferences.getPreferences(getActivity()).getPreferredKeyserver());
}
// Message is received after key is deleted
Handler returnHandler = new Handler() {
@Override
public void handleMessage(Message message) {
if (message.arg1 == DeleteKeyDialogFragment.MESSAGE_OKAY) {
Bundle data = message.getData();
if (data != null) {
DeleteResult result = data.getParcelable(DeleteResult.EXTRA_RESULT);
if (result != null) {
result.createNotify(getActivity()).show();
}
}
mode.finish();
}
}
};
// Create a new Messenger for the communication back
Messenger messenger = new Messenger(returnHandler);
DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger,
masterKeyIds);
deleteKeyDialog.show(getActivity().getSupportFragmentManager(), "deleteKeyDialog");
startActivityForResult(intent, REQUEST_DELETE);
}
@@ -620,14 +594,37 @@ public class KeyListFragment extends LoaderFragment
mConsolidateOpHelper.handleActivityResult(requestCode, resultCode, data);
}
if (requestCode == REQUEST_ACTION) {
// if a result has been returned, display a notify
if (data != null && data.hasExtra(OperationResult.EXTRA_RESULT)) {
OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT);
result.createNotify(getActivity()).show();
} else {
super.onActivityResult(requestCode, resultCode, data);
}
switch (requestCode) {
case REQUEST_DELETE:
if (mActionMode != null) {
mActionMode.finish();
}
if (data != null && data.hasExtra(OperationResult.EXTRA_RESULT)) {
OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT);
result.createNotify(getActivity()).show();
} else {
super.onActivityResult(requestCode, resultCode, data);
}
break;
case REQUEST_ACTION:
// if a result has been returned, display a notify
if (data != null && data.hasExtra(OperationResult.EXTRA_RESULT)) {
OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT);
result.createNotify(getActivity()).show();
} else {
super.onActivityResult(requestCode, resultCode, data);
}
break;
case REQUEST_VIEW_KEY:
if (data != null && data.hasExtra(OperationResult.EXTRA_RESULT)) {
OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT);
result.createNotify(getActivity()).show();
} else {
super.onActivityResult(requestCode, resultCode, data);
}
break;
}
}

View File

@@ -33,21 +33,25 @@ import org.sufficientlysecure.keychain.util.Preferences;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Date;
/**
* This class provides a communication interface to OpenPGP applications on ISO SmartCard compliant
* NFC devices.
* <p/>
* For the full specs, see http://g10code.com/docs/openpgp-card-2.0.pdf
* NOTE: If no CryptoInputParcel is passed via EXTRA_CRYPTO_INPUT, the CryptoInputParcel is created
* internally and is NOT meant to be used by signing operations before adding signature time
*/
public class NfcOperationActivity extends BaseNfcActivity {
public static final String EXTRA_REQUIRED_INPUT = "required_input";
public static final String EXTRA_CRYPTO_INPUT = "crypto_input";
// passthrough for OpenPgpService
public static final String EXTRA_SERVICE_INTENT = "data";
public static final String RESULT_DATA = "result_data";
public static final String RESULT_CRYPTO_INPUT = "result_data";
public ViewAnimator vAnimator;
public TextView vErrorText;
@@ -67,6 +71,15 @@ public class NfcOperationActivity extends BaseNfcActivity {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
mInputParcel = getIntent().getParcelableExtra(EXTRA_CRYPTO_INPUT);
if (mInputParcel == null) {
// for compatibility when used from OpenPgpService
// (or any place other than CryptoOperationHelper)
// NOTE: This CryptoInputParcel cannot be used for signing without adding signature time
mInputParcel = new CryptoInputParcel();
}
setTitle(R.string.nfc_text);
vAnimator = (ViewAnimator) findViewById(R.id.view_animator);
@@ -112,8 +125,6 @@ public class NfcOperationActivity extends BaseNfcActivity {
@Override
protected void doNfcInBackground() throws IOException {
mInputParcel = new CryptoInputParcel(mRequiredInput.mSignatureTime);
switch (mRequiredInput.mType) {
case NFC_DECRYPT: {
for (int i = 0; i < mRequiredInput.mInputData.length; i++) {
@@ -124,6 +135,9 @@ public class NfcOperationActivity extends BaseNfcActivity {
break;
}
case NFC_SIGN: {
if (mInputParcel.getSignatureTime() == null) {
mInputParcel.addSignatureTime(new Date());
}
for (int i = 0; i < mRequiredInput.mInputData.length; i++) {
byte[] hash = mRequiredInput.mInputData[i];
int algo = mRequiredInput.mSignAlgos[i];
@@ -218,11 +232,16 @@ public class NfcOperationActivity extends BaseNfcActivity {
@Override
protected void onNfcPostExecute() throws IOException {
if (mServiceIntent != null) {
// if we're triggered by OpenPgpService
CryptoInputParcelCacheService.addCryptoInputParcel(this, mServiceIntent, mInputParcel);
mServiceIntent.putExtra(EXTRA_CRYPTO_INPUT,
getIntent().getParcelableExtra(EXTRA_CRYPTO_INPUT));
setResult(RESULT_OK, mServiceIntent);
} else {
Intent result = new Intent();
result.putExtra(NfcOperationActivity.RESULT_DATA, mInputParcel);
result.putExtra(RESULT_CRYPTO_INPUT, mInputParcel);
// send back the CryptoInputParcel we receive, to conform with the pattern in
// CryptoOperationHelper
setResult(RESULT_OK, result);
}

View File

@@ -66,17 +66,22 @@ import org.sufficientlysecure.keychain.util.Preferences;
/**
* We can not directly create a dialog on the application context.
* This activity encapsulates a DialogFragment to emulate a dialog.
* NOTE: If no CryptoInputParcel is passed via EXTRA_CRYPTO_INPUT, the CryptoInputParcel is created
* internally and is NOT meant to be used by signing operations before adding a signature time
*/
public class PassphraseDialogActivity extends FragmentActivity {
public static final String RESULT_CRYPTO_INPUT = "result_data";
public static final String EXTRA_REQUIRED_INPUT = "required_input";
public static final String EXTRA_SUBKEY_ID = "secret_key_id";
public static final String EXTRA_CRYPTO_INPUT = "crypto_input";
// special extra for OpenPgpService
public static final String EXTRA_SERVICE_INTENT = "data";
private long mSubKeyId;
private CryptoInputParcel mCryptoInputParcel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -90,6 +95,15 @@ public class PassphraseDialogActivity extends FragmentActivity {
);
}
mCryptoInputParcel = getIntent().getParcelableExtra(EXTRA_CRYPTO_INPUT);
if (mCryptoInputParcel == null) {
// not all usages of PassphraseActivity are from CryptoInputOperation
// NOTE: This CryptoInputParcel cannot be used for signing operations without setting
// signature time
mCryptoInputParcel = new CryptoInputParcel();
}
// this activity itself has no content view (see manifest)
if (getIntent().hasExtra(EXTRA_SUBKEY_ID)) {
@@ -113,7 +127,8 @@ public class PassphraseDialogActivity extends FragmentActivity {
SecretKeyType.PASSPHRASE_EMPTY) {
// also return passphrase back to activity
Intent returnIntent = new Intent();
returnIntent.putExtra(RESULT_CRYPTO_INPUT, new CryptoInputParcel(new Passphrase("")));
mCryptoInputParcel.mPassphrase = new Passphrase("");
returnIntent.putExtra(RESULT_CRYPTO_INPUT, mCryptoInputParcel);
setResult(RESULT_OK, returnIntent);
finish();
return;
@@ -330,11 +345,16 @@ public class PassphraseDialogActivity extends FragmentActivity {
public void onClick(View v) {
final Passphrase passphrase = new Passphrase(mPassphraseEditText);
CryptoInputParcel cryptoInputParcel =
((PassphraseDialogActivity) getActivity()).mCryptoInputParcel;
// Early breakout if we are dealing with a symmetric key
if (mSecretRing == null) {
PassphraseCacheService.addCachedPassphrase(getActivity(),
Constants.key.symmetric, Constants.key.symmetric, passphrase,
getString(R.string.passp_cache_notif_pwd));
if (cryptoInputParcel.mCachePassphrase) {
PassphraseCacheService.addCachedPassphrase(getActivity(),
Constants.key.symmetric, Constants.key.symmetric, passphrase,
getString(R.string.passp_cache_notif_pwd));
}
finishCaching(passphrase);
return;
@@ -387,15 +407,24 @@ public class PassphraseDialogActivity extends FragmentActivity {
return;
}
// cache the new passphrase
Log.d(Constants.TAG, "Everything okay! Caching entered passphrase");
// cache the new passphrase as specified in CryptoInputParcel
Log.d(Constants.TAG, "Everything okay!");
try {
PassphraseCacheService.addCachedPassphrase(getActivity(),
mSecretRing.getMasterKeyId(), mSubKeyId, passphrase,
mSecretRing.getPrimaryUserIdWithFallback());
} catch (PgpKeyNotFoundException e) {
Log.e(Constants.TAG, "adding of a passphrase failed", e);
CryptoInputParcel cryptoInputParcel
= ((PassphraseDialogActivity) getActivity()).mCryptoInputParcel;
if (cryptoInputParcel.mCachePassphrase) {
Log.d(Constants.TAG, "Caching entered passphrase");
try {
PassphraseCacheService.addCachedPassphrase(getActivity(),
mSecretRing.getMasterKeyId(), mSubKeyId, passphrase,
mSecretRing.getPrimaryUserIdWithFallback());
} catch (PgpKeyNotFoundException e) {
Log.e(Constants.TAG, "adding of a passphrase failed", e);
}
} else {
Log.d(Constants.TAG, "Not caching entered passphrase!");
}
finishCaching(passphrase);
@@ -411,9 +440,12 @@ public class PassphraseDialogActivity extends FragmentActivity {
return;
}
CryptoInputParcel inputParcel = new CryptoInputParcel(null, passphrase);
CryptoInputParcel inputParcel =
((PassphraseDialogActivity) getActivity()).mCryptoInputParcel;
inputParcel.mPassphrase = passphrase;
if (mServiceIntent != null) {
CryptoInputParcelCacheService.addCryptoInputParcel(getActivity(), mServiceIntent, inputParcel);
CryptoInputParcelCacheService.addCryptoInputParcel(getActivity(), mServiceIntent,
inputParcel);
getActivity().setResult(RESULT_OK, mServiceIntent);
} else {
// also return passphrase back to activity

View File

@@ -0,0 +1,88 @@
/*
* Copyright (C) 2013-2015 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
* Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
*
* 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.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentActivity;
import android.view.ContextThemeWrapper;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.ui.dialog.CustomAlertDialogBuilder;
public class RetryUploadDialogActivity extends FragmentActivity {
public static final String EXTRA_CRYPTO_INPUT = "extra_crypto_input";
public static final String RESULT_CRYPTO_INPUT = "result_crypto_input";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
UploadRetryDialogFragment.newInstance().show(getSupportFragmentManager(),
"uploadRetryDialog");
}
public static class UploadRetryDialogFragment extends DialogFragment {
public static UploadRetryDialogFragment newInstance() {
return new UploadRetryDialogFragment();
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
ContextThemeWrapper theme = new ContextThemeWrapper(getActivity(),
R.style.Theme_AppCompat_Light_Dialog);
CustomAlertDialogBuilder dialogBuilder = new CustomAlertDialogBuilder(theme);
dialogBuilder.setTitle(R.string.retry_up_dialog_title);
dialogBuilder.setMessage(R.string.retry_up_dialog_message);
dialogBuilder.setNegativeButton(R.string.retry_up_dialog_btn_cancel,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
getActivity().setResult(RESULT_CANCELED);
getActivity().finish();
}
});
dialogBuilder.setPositiveButton(R.string.retry_up_dialog_btn_reupload,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent();
intent.putExtra(RESULT_CRYPTO_INPUT, getActivity()
.getIntent().getParcelableExtra(EXTRA_CRYPTO_INPUT));
getActivity().setResult(RESULT_OK, intent);
getActivity().finish();
}
});
return dialogBuilder.show();
}
}
}

View File

@@ -31,8 +31,6 @@ import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Messenger;
import android.provider.ContactsContract;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.FragmentManager;
@@ -52,7 +50,9 @@ import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.getbase.floatingactionbutton.FloatingActionButton;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
@@ -65,10 +65,8 @@ import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
import org.sufficientlysecure.keychain.service.ServiceProgressHandler.MessageStatus;
import org.sufficientlysecure.keychain.ui.base.BaseNfcActivity;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
import org.sufficientlysecure.keychain.ui.util.FormattingUtils;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State;
@@ -85,7 +83,6 @@ import org.sufficientlysecure.keychain.util.Preferences;
import java.io.IOException;
import java.util.ArrayList;
public class ViewKeyActivity extends BaseNfcActivity implements
LoaderManager.LoaderCallbacks<Cursor>,
CryptoOperationHelper.Callback<ImportKeyringParcel, ImportKeyResult> {
@@ -97,6 +94,7 @@ public class ViewKeyActivity extends BaseNfcActivity implements
static final int REQUEST_QR_FINGERPRINT = 1;
static final int REQUEST_BACKUP = 2;
static final int REQUEST_CERTIFY = 3;
static final int REQUEST_DELETE = 4;
public static final String EXTRA_DISPLAY_RESULT = "display_result";
@@ -419,27 +417,18 @@ public class ViewKeyActivity extends BaseNfcActivity implements
}
private void deleteKey() {
new Handler().post(new Runnable() {
@Override
public void run() {
// Message is received after key is deleted
Handler returnHandler = new Handler() {
@Override
public void handleMessage(Message message) {
if (message.arg1 == MessageStatus.OKAY.ordinal()) {
setResult(RESULT_CANCELED);
finish();
}
}
};
Intent deleteIntent = new Intent(this, DeleteKeyDialogActivity.class);
// Create a new Messenger for the communication back
Messenger messenger = new Messenger(returnHandler);
DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger,
new long[]{ mMasterKeyId });
deleteKeyDialog.show(getSupportFragmentManager(), "deleteKeyDialog");
}
});
deleteIntent.putExtra(DeleteKeyDialogActivity.EXTRA_DELETE_MASTER_KEY_IDS,
new long[]{mMasterKeyId});
deleteIntent.putExtra(DeleteKeyDialogActivity.EXTRA_HAS_SECRET, mIsSecret);
if (mIsSecret) {
// for upload in case key is secret
deleteIntent.putExtra(DeleteKeyDialogActivity.EXTRA_KEYSERVER,
Preferences.getPreferences(this).getPreferredKeyserver());
}
startActivityForResult(deleteIntent, REQUEST_DELETE);
}
@Override
@@ -487,6 +476,12 @@ public class ViewKeyActivity extends BaseNfcActivity implements
}
return;
}
case REQUEST_DELETE: {
setResult(RESULT_OK, data);
finish();
return;
}
}
super.onActivityResult(requestCode, resultCode, data);

View File

@@ -27,6 +27,7 @@ import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
import org.sufficientlysecure.keychain.keyimport.Keyserver;
import org.sufficientlysecure.keychain.operations.results.GetKeyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ParcelableProxy;
@@ -48,12 +49,13 @@ public class ImportKeysListCloudLoader
private AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> mEntryListWrapper;
/**
* Pass a parcelableProxy to specify an explicit proxy, otherwise will fetch from preferences
* Searches a keyserver as specified in cloudPrefs, using an explicit proxy if passed
*
* @param context
* @param serverQuery
* @param cloudPrefs
* @param parcelableProxy
* @param serverQuery string to search on servers for. If is a fingerprint,
* will enforce fingerprint check
* @param cloudPrefs contains keyserver to search on, whether to search on the keyserver,
* and whether to search keybase.io
* @param parcelableProxy explicit proxy to use. If null, will retrieve from preferences
*/
public ImportKeysListCloudLoader(Context context, String serverQuery, Preferences.CloudSearchPrefs cloudPrefs,
@Nullable ParcelableProxy parcelableProxy) {
@@ -121,7 +123,8 @@ public class ImportKeysListCloudLoader
// user needs to enable/install orbot
mEntryList.clear();
GetKeyResult pendingResult = new GetKeyResult(null,
RequiredInputParcel.createOrbotRequiredOperation());
RequiredInputParcel.createOrbotRequiredOperation(),
new CryptoInputParcel());
mEntryListWrapper = new AsyncTaskResultWrapper<>(mEntryList, pendingResult);
return;
}

View File

@@ -40,6 +40,7 @@ import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
import org.sufficientlysecure.keychain.ui.NfcOperationActivity;
import org.sufficientlysecure.keychain.ui.OrbotRequiredDialogActivity;
import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity;
import org.sufficientlysecure.keychain.ui.RetryUploadDialogActivity;
import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment;
import org.sufficientlysecure.keychain.util.Log;
@@ -76,6 +77,7 @@ public class CryptoOperationHelper<T extends Parcelable, S extends OperationResu
public static final int REQUEST_CODE_PASSPHRASE = 1;
public static final int REQUEST_CODE_NFC = 2;
public static final int REQUEST_CODE_ENABLE_ORBOT = 3;
public static final int REQUEST_CODE_RETRY_UPLOAD = 4;
private Integer mProgressMessageResource;
@@ -118,18 +120,14 @@ public class CryptoOperationHelper<T extends Parcelable, S extends OperationResu
Activity activity = mUseFragment ? mFragment.getActivity() : mActivity;
switch (requiredInput.mType) {
// TODO: Verify that all started activities add to cryptoInputParcel if necessary (like OrbotRequiredDialogActivity)
// don't forget to set mRequestedCode!
// always use CryptoOperationHelper.startActivityForResult!
case NFC_MOVE_KEY_TO_CARD:
case NFC_DECRYPT:
case NFC_SIGN: {
Intent intent = new Intent(activity, NfcOperationActivity.class);
intent.putExtra(NfcOperationActivity.EXTRA_REQUIRED_INPUT, requiredInput);
if (mUseFragment) {
mFragment.startActivityForResult(intent, mId + REQUEST_CODE_NFC);
} else {
activity.startActivityForResult(intent, mId + REQUEST_CODE_NFC);
}
intent.putExtra(NfcOperationActivity.EXTRA_CRYPTO_INPUT, cryptoInputParcel);
startActivityForResult(intent, REQUEST_CODE_NFC);
return;
}
@@ -137,22 +135,22 @@ public class CryptoOperationHelper<T extends Parcelable, S extends OperationResu
case PASSPHRASE_SYMMETRIC: {
Intent intent = new Intent(activity, PassphraseDialogActivity.class);
intent.putExtra(PassphraseDialogActivity.EXTRA_REQUIRED_INPUT, requiredInput);
if (mUseFragment) {
mFragment.startActivityForResult(intent, mId + REQUEST_CODE_PASSPHRASE);
} else {
activity.startActivityForResult(intent, mId + REQUEST_CODE_PASSPHRASE);
}
intent.putExtra(PassphraseDialogActivity.EXTRA_CRYPTO_INPUT, cryptoInputParcel);
startActivityForResult(intent, REQUEST_CODE_PASSPHRASE);
return;
}
case ENABLE_ORBOT: {
Intent intent = new Intent(activity, OrbotRequiredDialogActivity.class);
intent.putExtra(OrbotRequiredDialogActivity.EXTRA_CRYPTO_INPUT, cryptoInputParcel);
if (mUseFragment) {
mFragment.startActivityForResult(intent, mId + REQUEST_CODE_ENABLE_ORBOT);
} else {
activity.startActivityForResult(intent, mId + REQUEST_CODE_ENABLE_ORBOT);
}
startActivityForResult(intent, REQUEST_CODE_ENABLE_ORBOT);
return;
}
case UPLOAD_FAIL_RETRY: {
Intent intent = new Intent(activity, RetryUploadDialogActivity.class);
intent.putExtra(RetryUploadDialogActivity.EXTRA_CRYPTO_INPUT, cryptoInputParcel);
startActivityForResult(intent, REQUEST_CODE_RETRY_UPLOAD);
return;
}
@@ -162,6 +160,14 @@ public class CryptoOperationHelper<T extends Parcelable, S extends OperationResu
}
}
protected void startActivityForResult(Intent intent, int requestCode) {
if (mUseFragment) {
mFragment.startActivityForResult(intent, mId + requestCode);
} else {
mActivity.startActivityForResult(intent, mId + requestCode);
}
}
/**
* Attempts the result of an activity started by this helper. Returns true if requestCode is
* recognized, false otherwise.
@@ -174,6 +180,7 @@ public class CryptoOperationHelper<T extends Parcelable, S extends OperationResu
// this wasn't meant for us to handle
return false;
}
Log.d(Constants.TAG, "handling activity result in OperationHelper");
// filter out mId from requestCode
requestCode ^= mId;
@@ -188,7 +195,6 @@ public class CryptoOperationHelper<T extends Parcelable, S extends OperationResu
CryptoInputParcel cryptoInput =
data.getParcelableExtra(PassphraseDialogActivity.RESULT_CRYPTO_INPUT);
cryptoOperation(cryptoInput);
return true;
}
break;
}
@@ -196,9 +202,8 @@ public class CryptoOperationHelper<T extends Parcelable, S extends OperationResu
case REQUEST_CODE_NFC: {
if (resultCode == Activity.RESULT_OK && data != null) {
CryptoInputParcel cryptoInput =
data.getParcelableExtra(NfcOperationActivity.RESULT_DATA);
data.getParcelableExtra(NfcOperationActivity.RESULT_CRYPTO_INPUT);
cryptoOperation(cryptoInput);
return true;
}
break;
}
@@ -209,12 +214,22 @@ public class CryptoOperationHelper<T extends Parcelable, S extends OperationResu
data.getParcelableExtra(
OrbotRequiredDialogActivity.RESULT_CRYPTO_INPUT);
cryptoOperation(cryptoInput);
return true;
}
break;
}
case REQUEST_CODE_RETRY_UPLOAD: {
if (resultCode == Activity.RESULT_OK) {
CryptoInputParcel cryptoInput =
data.getParcelableExtra(
RetryUploadDialogActivity.RESULT_CRYPTO_INPUT);
cryptoOperation(cryptoInput);
}
break;
}
}
return false;
return true;
}
protected void dismissProgress() {
@@ -271,7 +286,7 @@ public class CryptoOperationHelper<T extends Parcelable, S extends OperationResu
final OperationResult result =
returnData.getParcelable(OperationResult.EXTRA_RESULT);
onHandleResult(result, cryptoInput);
onHandleResult(result);
}
}
@@ -301,15 +316,14 @@ public class CryptoOperationHelper<T extends Parcelable, S extends OperationResu
cryptoOperation(new CryptoInputParcel());
}
public void onHandleResult(OperationResult result, CryptoInputParcel oldCryptoInput) {
public void onHandleResult(OperationResult result) {
Log.d(Constants.TAG, "Handling result in OperationHelper success: " + result.success());
if (result instanceof InputPendingResult) {
InputPendingResult pendingResult = (InputPendingResult) result;
if (pendingResult.isPending()) {
RequiredInputParcel requiredInput = pendingResult.getRequiredInputParcel();
initiateInputActivity(requiredInput, oldCryptoInput);
initiateInputActivity(requiredInput, pendingResult.mCryptoInputParcel);
return;
}
}

View File

@@ -1,211 +0,0 @@
/*
* Copyright (C) 2013-2014 Dominik Schürmann <dominik@dominikschuermann.de>
*
* 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.dialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.DeleteResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.DeleteKeyringParcel;
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import org.sufficientlysecure.keychain.util.Log;
import java.util.HashMap;
public class DeleteKeyDialogFragment extends DialogFragment
implements CryptoOperationHelper.Callback<DeleteKeyringParcel, DeleteResult> {
private static final String ARG_MESSENGER = "messenger";
private static final String ARG_DELETE_MASTER_KEY_IDS = "delete_master_key_ids";
public static final int MESSAGE_OKAY = 1;
public static final int MESSAGE_ERROR = 0;
private TextView mMainMessage;
private View mInflateView;
private Messenger mMessenger;
// for CryptoOperationHelper.Callback
private long[] mMasterKeyIds;
private boolean mHasSecret;
private CryptoOperationHelper<DeleteKeyringParcel, DeleteResult> mDeleteOpHelper;
/**
* Creates new instance of this delete file dialog fragment
*/
public static DeleteKeyDialogFragment newInstance(Messenger messenger, long[] masterKeyIds) {
DeleteKeyDialogFragment frag = new DeleteKeyDialogFragment();
Bundle args = new Bundle();
args.putParcelable(ARG_MESSENGER, messenger);
args.putLongArray(ARG_DELETE_MASTER_KEY_IDS, masterKeyIds);
frag.setArguments(args);
return frag;
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (mDeleteOpHelper != null) {
mDeleteOpHelper.handleActivityResult(requestCode, resultCode, data);
}
super.onActivityResult(requestCode, resultCode, data);
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final FragmentActivity activity = getActivity();
mMessenger = getArguments().getParcelable(ARG_MESSENGER);
final long[] masterKeyIds = getArguments().getLongArray(ARG_DELETE_MASTER_KEY_IDS);
CustomAlertDialogBuilder builder = new CustomAlertDialogBuilder(activity);
// Setup custom View to display in AlertDialog
LayoutInflater inflater = activity.getLayoutInflater();
mInflateView = inflater.inflate(R.layout.view_key_delete_fragment, null);
builder.setView(mInflateView);
mMainMessage = (TextView) mInflateView.findViewById(R.id.mainMessage);
final boolean hasSecret;
// If only a single key has been selected
if (masterKeyIds.length == 1) {
long masterKeyId = masterKeyIds[0];
try {
HashMap<String, Object> data = new ProviderHelper(activity).getUnifiedData(
masterKeyId, new String[]{
KeyRings.USER_ID,
KeyRings.HAS_ANY_SECRET
}, new int[]{
ProviderHelper.FIELD_TYPE_STRING,
ProviderHelper.FIELD_TYPE_INTEGER
}
);
String name;
KeyRing.UserId mainUserId = KeyRing.splitUserId((String) data.get(KeyRings.USER_ID));
if (mainUserId.name != null) {
name = mainUserId.name;
} else {
name = getString(R.string.user_id_no_name);
}
hasSecret = ((Long) data.get(KeyRings.HAS_ANY_SECRET)) == 1;
if (hasSecret) {
// show title only for secret key deletions,
// see http://www.google.com/design/spec/components/dialogs.html#dialogs-behavior
builder.setTitle(getString(R.string.title_delete_secret_key, name));
mMainMessage.setText(getString(R.string.secret_key_deletion_confirmation, name));
} else {
mMainMessage.setText(getString(R.string.public_key_deletetion_confirmation, name));
}
} catch (ProviderHelper.NotFoundException e) {
dismiss();
return null;
}
} else {
mMainMessage.setText(R.string.key_deletion_confirmation_multi);
hasSecret = false;
}
builder.setPositiveButton(R.string.btn_delete, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
mMasterKeyIds = masterKeyIds;
mHasSecret = hasSecret;
mDeleteOpHelper = new CryptoOperationHelper<>
(1, DeleteKeyDialogFragment.this, DeleteKeyDialogFragment.this,
R.string.progress_deleting);
mDeleteOpHelper.cryptoOperation();
// do NOT dismiss here, it'll give
// OperationHelper a null fragmentManager
// dismiss();
}
});
builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
dismiss();
}
});
return builder.show();
}
@Override
public DeleteKeyringParcel createOperationInput() {
return new DeleteKeyringParcel(mMasterKeyIds, mHasSecret);
}
@Override
public void onCryptoOperationSuccess(DeleteResult result) {
handleResult(result);
}
@Override
public void onCryptoOperationCancelled() {
}
@Override
public void onCryptoOperationError(DeleteResult result) {
handleResult(result);
}
@Override
public boolean onCryptoSetProgress(String msg, int progress, int max) {
return false;
}
public void handleResult(DeleteResult result) {
try {
Bundle data = new Bundle();
data.putParcelable(OperationResult.EXTRA_RESULT, result);
Message msg = Message.obtain();
msg.arg1 = ServiceProgressHandler.MessageStatus.OKAY.ordinal();
msg.setData(data);
mMessenger.send(msg);
} catch (RemoteException e) {
Log.e(Constants.TAG, "messenger error", e);
}
dismiss();
}
}

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="16dp"
android:paddingLeft="24dp"
android:paddingRight="24dp"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/del_rev_dialog_message"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:layout_marginBottom="16dp"/>
<Spinner
android:id="@+id/spinner"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:drawSelectorOnTop="true"
android:entries="@array/rev_del_dialog_entries"/>
</LinearLayout>

View File

@@ -37,6 +37,10 @@
<item>@string/pref_proxy_type_value_http</item>
<item>@string/pref_proxy_type_value_socks</item>
</string-array>
<string-array name="rev_del_dialog_entries" translatable="true">
<item>@string/del_rev_dialog_choice_rev_upload</item>
<item>@string/del_rev_dialog_choice_delete</item>
</string-array>
<string-array name="rsa_key_size_spinner_values" translatable="false">
<item>@string/key_size_2048</item>
<item>@string/key_size_4096</item>

View File

@@ -361,6 +361,7 @@
<string name="progress_cancelling">"cancelling…"</string>
<string name="progress_saving">"saving…"</string>
<string name="progress_importing">"importing…"</string>
<string name="progress_revoking_uploading">"Revoking and uploading key…"</string>
<string name="progress_updating">"Updating keys…"</string>
<string name="progress_exporting">"exporting…"</string>
<string name="progress_uploading">"uploading…"</string>
@@ -524,6 +525,12 @@
<string name="delete_nothing">"Nothing to delete."</string>
<string name="delete_cancelled">"Delete operation cancelled."</string>
<!-- Revoke result toast (snackbar) -->
<string name="revoke_ok">"Successfully revoked key."</string>
<string name="revoke_fail">"Error revoking key!"</string>
<string name="revoke_nothing">"Nothing to revoke."</string>
<string name="revoke_cancelled">"Revoke operation cancelled."</string>
<!-- Certify result toast -->
<plurals name="certify_keys_ok">
<item quantity="one">"Successfully certified key%2$s."</item>
@@ -584,6 +591,23 @@
<string name="share_qr_code_dialog_title">"Share with QR Code"</string>
<string name="share_nfc_dialog">"Share with NFC"</string>
<!-- retry upload dialog -->
<string name="retry_up_dialog_title">"Upload failed"</string>
<string name="retry_up_dialog_message">"Upload failed. Would you like to retry the operation?"</string>
<string name="retry_up_dialog_btn_reupload">"Retry Operation"</string>
<string name="retry_up_dialog_btn_cancel">"Cancel Operation"</string>
<!-- Delete or revoke private key dialog -->
<string name="del_rev_dialog_message">"If you would no longer like to use this key, it should be revoked and uploaded. Select delete only if you wish to remove the key from OpenKeychain but continue to use it from somewhere else."</string>
<string name="del_rev_dialog_title">"Revoke/Delete key \"%s\""</string>
<string name="del_rev_dialog_btn_revoke">"Revoke and upload"</string>
<string name="del_rev_dialog_btn_delete">"Delete only"</string>
<!-- Delete Or Revoke Dialog spinner -->
<string name="del_rev_dialog_choice_delete">"Delete only"</string>
<string name="del_rev_dialog_choice_rev_upload">"Revoke and Upload"</string>
<!-- Key list -->
<plurals name="key_list_selected_keys">
<item quantity="one">"1 key selected."</item>
@@ -1072,6 +1096,7 @@
<string name="msg_ed_caching_new">"Caching new password"</string>
<string name="msg_ed_error_no_parcel">"Missing SaveKeyringParcel! (this is a bug, please report)"</string>
<string name="msg_ed_error_key_not_found">"Key not found!"</string>
<string name="msg_ed_error_extract_public_upload">"Error extracting public key for upload!"</string>
<string name="msg_ed_fetching">"Fetching key to modify (%s)"</string>
<string name="msg_ed_success">"Key operation successful"</string>
@@ -1270,6 +1295,13 @@
<item quantity="other">"Failed to delete %d keys"</item>
</plurals>
<string name="msg_revoke_error_empty">"Nothing to revoke!"</string>
<string name="msg_revoke_error_multi_secret">"Secret keys can only be revoked individually!"</string>
<string name="msg_revoke_error_not_found">"Cannot find key to revoke!"</string>
<string name="msg_revoke_key">"Revoking key %s"</string>
<string name="msg_revoke_key_fail">"Failed revoking key"</string>
<string name="msg_revoke_ok">"Successfully revoked key"</string>
<string name="msg_acc_saved">"Account saved"</string>
<string name="msg_download_success">"Downloaded successfully!"</string>

View File

@@ -52,6 +52,7 @@ import org.sufficientlysecure.keychain.util.TestingUtils;
import java.io.PrintStream;
import java.security.Security;
import java.util.ArrayList;
import java.util.Date;
import java.util.Random;
@RunWith(RobolectricGradleTestRunner.class)
@@ -158,7 +159,7 @@ public class CertifyOperationTest {
CertifyActionsParcel actions = new CertifyActionsParcel(mStaticRing1.getMasterKeyId());
actions.add(new CertifyAction(mStaticRing2.getMasterKeyId(),
mStaticRing2.getPublicKey().getUnorderedUserIds()));
CertifyResult result = op.execute(actions, new CryptoInputParcel(mKeyPhrase1));
CertifyResult result = op.execute(actions, new CryptoInputParcel(new Date(), mKeyPhrase1));
Assert.assertTrue("certification must succeed", result.success());
@@ -186,7 +187,7 @@ public class CertifyOperationTest {
CertifyActionsParcel actions = new CertifyActionsParcel(mStaticRing1.getMasterKeyId());
actions.add(new CertifyAction(mStaticRing2.getMasterKeyId(), null,
mStaticRing2.getPublicKey().getUnorderedUserAttributes()));
CertifyResult result = op.execute(actions, new CryptoInputParcel(mKeyPhrase1));
CertifyResult result = op.execute(actions, new CryptoInputParcel(new Date(), mKeyPhrase1));
Assert.assertTrue("certification must succeed", result.success());
@@ -209,7 +210,7 @@ public class CertifyOperationTest {
actions.add(new CertifyAction(mStaticRing1.getMasterKeyId(),
mStaticRing2.getPublicKey().getUnorderedUserIds()));
CertifyResult result = op.execute(actions, new CryptoInputParcel(mKeyPhrase1));
CertifyResult result = op.execute(actions, new CryptoInputParcel(new Date(), mKeyPhrase1));
Assert.assertFalse("certification with itself must fail!", result.success());
Assert.assertTrue("error msg must be about self certification",
@@ -228,7 +229,8 @@ public class CertifyOperationTest {
uids.add("nonexistent");
actions.add(new CertifyAction(1234L, uids));
CertifyResult result = op.execute(actions, new CryptoInputParcel(mKeyPhrase1));
CertifyResult result = op.execute(actions, new CryptoInputParcel(new Date(),
mKeyPhrase1));
Assert.assertFalse("certification of nonexistent key must fail", result.success());
Assert.assertTrue("must contain error msg about not found",
@@ -240,7 +242,8 @@ public class CertifyOperationTest {
actions.add(new CertifyAction(mStaticRing1.getMasterKeyId(),
mStaticRing2.getPublicKey().getUnorderedUserIds()));
CertifyResult result = op.execute(actions, new CryptoInputParcel(mKeyPhrase1));
CertifyResult result = op.execute(actions, new CryptoInputParcel(new Date(),
mKeyPhrase1));
Assert.assertFalse("certification of nonexistent key must fail", result.success());
Assert.assertTrue("must contain error msg about not found",

View File

@@ -25,6 +25,7 @@ import java.security.Security;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Date;
import org.junit.Assert;
import org.junit.Before;
@@ -160,7 +161,8 @@ public class PgpEncryptDecryptTest {
b.setSymmetricPassphrase(mPassphrase);
b.setSymmetricEncryptionAlgorithm(PGPEncryptedData.AES_128);
PgpSignEncryptResult result = op.execute(b, new CryptoInputParcel(), data, out);
PgpSignEncryptResult result = op.execute(b, new CryptoInputParcel(new Date()),
data, out);
Assert.assertTrue("encryption must succeed", result.success());
@@ -269,7 +271,8 @@ public class PgpEncryptDecryptTest {
input.setEncryptionMasterKeyIds(new long[] { mStaticRing1.getMasterKeyId() });
input.setSymmetricEncryptionAlgorithm(PGPEncryptedData.AES_128);
PgpSignEncryptResult result = op.execute(input, new CryptoInputParcel(), data, out);
PgpSignEncryptResult result = op.execute(input, new CryptoInputParcel(new Date()),
data, out);
Assert.assertTrue("encryption must succeed", result.success());
ciphertext = out.toByteArray();
@@ -359,7 +362,8 @@ public class PgpEncryptDecryptTest {
input.setEncryptionMasterKeyIds(new long[] { mStaticRing1.getMasterKeyId() });
input.setSymmetricEncryptionAlgorithm(PGPEncryptedData.AES_128);
PgpSignEncryptResult result = op.execute(input, new CryptoInputParcel(), data, out);
PgpSignEncryptResult result = op.execute(input, new CryptoInputParcel(new Date()),
data, out);
Assert.assertTrue("encryption must succeed", result.success());
ciphertext = out.toByteArray();
@@ -393,7 +397,8 @@ public class PgpEncryptDecryptTest {
SaveKeyringParcel parcel = new SaveKeyringParcel(mStaticRing1.getMasterKeyId(), mStaticRing1.getFingerprint());
parcel.mRevokeSubKeys.add(KeyringTestingHelper.getSubkeyId(mStaticRing1, 2));
UncachedKeyRing modified = PgpKeyOperationTest.applyModificationWithChecks(parcel, mStaticRing1,
new ArrayList<RawPacket>(), new ArrayList<RawPacket>(), new CryptoInputParcel(mKeyPhrase1));
new ArrayList<RawPacket>(), new ArrayList<RawPacket>(),
new CryptoInputParcel(new Date(), mKeyPhrase1));
ProviderHelper providerHelper = new ProviderHelper(RuntimeEnvironment.application);
providerHelper.saveSecretKeyRing(modified, new ProgressScaler());
@@ -413,7 +418,8 @@ public class PgpEncryptDecryptTest {
input.setEncryptionMasterKeyIds(new long[] { mStaticRing1.getMasterKeyId() });
input.setSymmetricEncryptionAlgorithm(PGPEncryptedData.AES_128);
PgpSignEncryptResult result = op.execute(input, new CryptoInputParcel(), data, out);
PgpSignEncryptResult result = op.execute(input, new CryptoInputParcel(new Date()),
data, out);
Assert.assertTrue("encryption must succeed", result.success());
ciphertext = out.toByteArray();
@@ -457,7 +463,8 @@ public class PgpEncryptDecryptTest {
});
b.setSymmetricEncryptionAlgorithm(PGPEncryptedData.AES_128);
PgpSignEncryptResult result = op.execute(b, new CryptoInputParcel(), data, out);
PgpSignEncryptResult result = op.execute(b, new CryptoInputParcel(new Date()),
data, out);
Assert.assertTrue("encryption must succeed", result.success());
ciphertext = out.toByteArray();
@@ -575,7 +582,8 @@ public class PgpEncryptDecryptTest {
b.setSignatureSubKeyId(KeyringTestingHelper.getSubkeyId(mStaticRing1, 1));
b.setSymmetricEncryptionAlgorithm(PGPEncryptedData.AES_128);
PgpSignEncryptResult result = op.execute(b, new CryptoInputParcel(mKeyPhrase1), data, out);
PgpSignEncryptResult result = op.execute(b,
new CryptoInputParcel(new Date(), mKeyPhrase1), data, out);
Assert.assertTrue("encryption must succeed", result.success());
ciphertext = out.toByteArray();
@@ -655,7 +663,8 @@ public class PgpEncryptDecryptTest {
// this only works with ascii armored output!
b.setEnableAsciiArmorOutput(true);
b.setCharset("iso-2022-jp");
PgpSignEncryptResult result = op.execute(b, new CryptoInputParcel(), data, out);
PgpSignEncryptResult result = op.execute(b, new CryptoInputParcel(new Date()),
data, out);
Assert.assertTrue("encryption must succeed", result.success());
ciphertext = out.toByteArray();

View File

@@ -554,7 +554,7 @@ public class UncachedKeyringCanonicalizeTest {
CanonicalizedSecretKey masterSecretKey = canonicalized.getSecretKey();
masterSecretKey.unlock(new Passphrase());
PGPPublicKey masterPublicKey = masterSecretKey.getPublicKey();
CryptoInputParcel cryptoInput = new CryptoInputParcel();
CryptoInputParcel cryptoInput = new CryptoInputParcel(new Date());
PGPSignature cert = PgpKeyOperation.generateSubkeyBindingSignature(
PgpKeyOperation.getSignatureGenerator(masterSecretKey.getSecretKey(), cryptoInput),
cryptoInput.getSignatureTime(),

View File

@@ -194,11 +194,11 @@ public class UncachedKeyringMergeTest {
parcel.reset();
parcel.mAddUserIds.add("flim");
modifiedA = op.modifySecretKeyRing(secretRing, new CryptoInputParcel(new Passphrase()), parcel).getRing();
modifiedA = op.modifySecretKeyRing(secretRing, new CryptoInputParcel(new Date(), new Passphrase()), parcel).getRing();
parcel.reset();
parcel.mAddUserIds.add("flam");
modifiedB = op.modifySecretKeyRing(secretRing, new CryptoInputParcel(new Passphrase()), parcel).getRing();
modifiedB = op.modifySecretKeyRing(secretRing, new CryptoInputParcel(new Date(), new Passphrase()), parcel).getRing();
}
{ // merge A into base
@@ -235,8 +235,8 @@ public class UncachedKeyringMergeTest {
parcel.reset();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
Algorithm.RSA, 1024, null, KeyFlags.SIGN_DATA, 0L));
modifiedA = op.modifySecretKeyRing(secretRing, new CryptoInputParcel(new Passphrase()), parcel).getRing();
modifiedB = op.modifySecretKeyRing(secretRing, new CryptoInputParcel(new Passphrase()), parcel).getRing();
modifiedA = op.modifySecretKeyRing(secretRing, new CryptoInputParcel(new Date(), new Passphrase()), parcel).getRing();
modifiedB = op.modifySecretKeyRing(secretRing, new CryptoInputParcel(new Date(), new Passphrase()), parcel).getRing();
subKeyIdA = KeyringTestingHelper.getSubkeyId(modifiedA, 2);
subKeyIdB = KeyringTestingHelper.getSubkeyId(modifiedB, 2);
@@ -277,7 +277,7 @@ public class UncachedKeyringMergeTest {
parcel.mRevokeSubKeys.add(KeyringTestingHelper.getSubkeyId(ringA, 1));
CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(
ringA.getEncoded(), false, 0);
modified = op.modifySecretKeyRing(secretRing, new CryptoInputParcel(new Passphrase()), parcel).getRing();
modified = op.modifySecretKeyRing(secretRing, new CryptoInputParcel(new Date(), new Passphrase()), parcel).getRing();
}
{
@@ -376,7 +376,7 @@ public class UncachedKeyringMergeTest {
CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(
ringA.getEncoded(), false, 0);
modified = op.modifySecretKeyRing(secretRing, new CryptoInputParcel(new Passphrase()), parcel).getRing();
modified = op.modifySecretKeyRing(secretRing, new CryptoInputParcel(new Date(), new Passphrase()), parcel).getRing();
}
{