Work on new result handling (WIP)

This commit is contained in:
Dominik Schürmann 2014-09-17 13:45:16 +02:00
parent 3defd194aa
commit d686c55a0a
14 changed files with 308 additions and 182 deletions

View File

@ -0,0 +1,11 @@
package org.sufficientlysecure.keychain.pgp;
public interface PassphraseCacheInterface {
public static class NoSecretKeyException extends Exception {
public NoSecretKeyException() {
}
}
public String getCachedPassphrase(long masterKeyId) throws NoSecretKeyException;
}

View File

@ -72,7 +72,7 @@ import java.util.Set;
*/
public class PgpDecryptVerify {
private ProviderHelper mProviderHelper;
private PassphraseCache mPassphraseCache;
private PassphraseCacheInterface mPassphraseCache;
private InputData mData;
private OutputStream mOutStream;
@ -101,7 +101,7 @@ public class PgpDecryptVerify {
public static class Builder {
// mandatory parameter
private ProviderHelper mProviderHelper;
private PassphraseCache mPassphraseCache;
private PassphraseCacheInterface mPassphraseCache;
private InputData mData;
private OutputStream mOutStream;
@ -113,12 +113,12 @@ public class PgpDecryptVerify {
private boolean mDecryptMetadataOnly = false;
private byte[] mDecryptedSessionKey = null;
public Builder(ProviderHelper providerHelper, PassphraseCache passphraseCache,
public Builder(ProviderHelper providerHelper, PassphraseCacheInterface passphraseCache,
InputData data, OutputStream outStream) {
this.mProviderHelper = providerHelper;
this.mPassphraseCache = passphraseCache;
this.mData = data;
this.mOutStream = outStream;
mProviderHelper = providerHelper;
mPassphraseCache = passphraseCache;
mData = data;
mOutStream = outStream;
}
public Builder setProgressable(Progressable progressable) {
@ -176,16 +176,6 @@ public class PgpDecryptVerify {
}
}
public interface PassphraseCache {
public String getCachedPassphrase(long masterKeyId)
throws NoSecretKeyException;
}
public static class NoSecretKeyException extends Exception {
public NoSecretKeyException() {
}
}
/**
* Decrypts and/or verifies data based on parameters of class
*/
@ -322,7 +312,7 @@ public class PgpDecryptVerify {
// returns "" if key has no passphrase
mPassphrase = mPassphraseCache.getCachedPassphrase(subKeyId);
log.add(LogType.MSG_DC_PASS_CACHED, indent +1);
} catch (NoSecretKeyException e) {
} catch (PassphraseCacheInterface.NoSecretKeyException e) {
log.add(LogType.MSG_DC_ERROR_NO_KEY, indent +1);
return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
}

View File

@ -59,6 +59,7 @@ import java.util.LinkedList;
*/
public class PgpSignEncrypt {
private ProviderHelper mProviderHelper;
private PassphraseCacheInterface mPassphraseCache;
private String mVersionHeader;
private InputData mData;
private OutputStream mOutStream;
@ -93,6 +94,7 @@ public class PgpSignEncrypt {
private PgpSignEncrypt(Builder builder) {
// private Constructor can only be called from Builder
this.mProviderHelper = builder.mProviderHelper;
this.mPassphraseCache = builder.mPassphraseCache;
this.mVersionHeader = builder.mVersionHeader;
this.mData = builder.mData;
this.mOutStream = builder.mOutStream;
@ -117,6 +119,7 @@ public class PgpSignEncrypt {
public static class Builder {
// mandatory parameter
private ProviderHelper mProviderHelper;
private PassphraseCacheInterface mPassphraseCache;
private InputData mData;
private OutputStream mOutStream;
@ -138,8 +141,10 @@ public class PgpSignEncrypt {
private byte[] mNfcSignedHash = null;
private Date mNfcCreationTimestamp = null;
public Builder(ProviderHelper providerHelper, InputData data, OutputStream outStream) {
public Builder(ProviderHelper providerHelper, PassphraseCacheInterface passphraseCache,
InputData data, OutputStream outStream) {
mProviderHelper = providerHelper;
mPassphraseCache = passphraseCache;
mData = data;
mOutStream = outStream;
}
@ -290,20 +295,15 @@ public class PgpSignEncrypt {
/* Get keys for signature generation for later usage */
CanonicalizedSecretKey signingKey = null;
long signKeyId;
if (enableSignature) {
// If we weren't handed a passphrase, throw early
if (mSignaturePassphrase == null) {
log.add(LogType.MSG_SE_ERROR_NO_PASSPHRASE, indent);
return new SignEncryptResult(SignEncryptResult.RESULT_ERROR, log);
}
try {
// fetch the indicated master key id (the one whose name we sign in)
CanonicalizedSecretKeyRing signingKeyRing =
mProviderHelper.getCanonicalizedSecretKeyRing(mSignatureMasterKeyId);
// fetch the specific subkey to sign with, or just use the master key if none specified
long signKeyId = mSignatureSubKeyId != null ? mSignatureSubKeyId : mSignatureMasterKeyId;
signKeyId = mSignatureSubKeyId != null ? mSignatureSubKeyId : mSignatureMasterKeyId;
signingKey = signingKeyRing.getSecretKey(signKeyId);
// make sure it's a signing key alright!
} catch (ProviderHelper.NotFoundException e) {
@ -317,6 +317,28 @@ public class PgpSignEncrypt {
return new SignEncryptResult(SignEncryptResult.RESULT_ERROR, log);
}
// if no passphrase was explicitly set try to get it from the cache service
if (mSignaturePassphrase == null) {
try {
// returns "" if key has no passphrase
mSignaturePassphrase = mPassphraseCache.getCachedPassphrase(signKeyId);
// TODO
// log.add(LogType.MSG_DC_PASS_CACHED, indent + 1);
} catch (PassphraseCacheInterface.NoSecretKeyException e) {
// TODO
// log.add(LogType.MSG_DC_ERROR_NO_KEY, indent + 1);
return new SignEncryptResult(SignEncryptResult.RESULT_ERROR, log);
}
// if passphrase was not cached, return here indicating that a passphrase is missing!
if (mSignaturePassphrase == null) {
log.add(LogType.MSG_SE_PENDING_PASSPHRASE, indent + 1);
SignEncryptResult result = new SignEncryptResult(SignEncryptResult.RESULT_PENDING_PASSPHRASE, log);
result.setKeyIdPassphraseNeeded(signKeyId);
return result;
}
}
updateProgress(R.string.progress_extracting_signature_key, 0, 100);
try {
@ -369,10 +391,10 @@ public class PgpSignEncrypt {
log.add(LogType.MSG_SE_KEY_OK, indent + 1,
PgpKeyHelper.convertKeyIdToHex(id));
} catch (PgpGeneralException e) {
log.add(LogType.MSG_SE_KEY_WARN, indent +1,
log.add(LogType.MSG_SE_KEY_WARN, indent + 1,
PgpKeyHelper.convertKeyIdToHex(id));
} catch (ProviderHelper.NotFoundException e) {
log.add(LogType.MSG_SE_KEY_UNKNOWN, indent +1,
log.add(LogType.MSG_SE_KEY_UNKNOWN, indent + 1,
PgpKeyHelper.convertKeyIdToHex(id));
}
}
@ -407,9 +429,10 @@ public class PgpSignEncrypt {
/* actual encryption */
updateProgress(R.string.progress_encrypting, 8, 100);
log.add(enableSignature
? LogType.MSG_SE_SIGCRYPTING
: LogType.MSG_SE_ENCRYPTING,
indent);
? LogType.MSG_SE_SIGCRYPTING
: LogType.MSG_SE_ENCRYPTING,
indent
);
indent += 1;
encryptionOut = cPk.open(out, new byte[1 << 16]);

View File

@ -32,6 +32,7 @@ import org.openintents.openpgp.OpenPgpSignatureResult;
import org.openintents.openpgp.util.OpenPgpApi;
import org.sufficientlysecure.keychain.nfc.NfcActivity;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.PassphraseCacheInterface;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
import org.sufficientlysecure.keychain.service.results.DecryptVerifyResult;
@ -259,6 +260,17 @@ public class OpenPgpService extends RemoteService {
// sign-only
PgpSignEncrypt.Builder builder = new PgpSignEncrypt.Builder(
new ProviderHelper(getContext()),
new PassphraseCacheInterface() {
@Override
public String getCachedPassphrase(long masterKeyId) throws PassphraseCacheInterface.NoSecretKeyException {
try {
return PassphraseCacheService.getCachedPassphrase(
OpenPgpService.this, masterKeyId);
} catch (PassphraseCacheService.KeyNotFoundException e) {
throw new PassphraseCacheInterface.NoSecretKeyException();
}
}
},
inputData, os);
builder.setEnableAsciiArmorOutput(asciiArmor)
.setVersionHeader(PgpHelper.getVersionForHeader(this))
@ -271,24 +283,27 @@ public class OpenPgpService extends RemoteService {
builder.setCleartextInput(true);
// execute PGP operation!
SignEncryptResult result = builder.build().execute();
SignEncryptResult pgpResult = builder.build().execute();
if (result.isPending()) {
switch (result.getResult()) {
case SignEncryptResult.RESULT_PENDING_NFC:
// return PendingIntent to execute NFC activity
// pass through the signature creation timestamp to be used again on second execution
// of PgpSignEncrypt when we have the signed hash!
data.putExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, result.getNfcTimestamp().getTime());
return getNfcSignIntent(data, passphrase, result.getNfcHash(), result.getNfcAlgo());
default:
throw new Exception("Encountered unhandled pending state - please file a bug report!");
if (pgpResult.isPending()) {
if ((pgpResult.getResult() & SignEncryptResult.RESULT_PENDING_PASSPHRASE) ==
SignEncryptResult.RESULT_PENDING_PASSPHRASE) {
return getPassphraseIntent(data, pgpResult.getKeyIdPassphraseNeeded());
} else if ((pgpResult.getResult() & SignEncryptResult.RESULT_PENDING_NFC) ==
SignEncryptResult.RESULT_PENDING_NFC) {
// return PendingIntent to execute NFC activity
// pass through the signature creation timestamp to be used again on second execution
// of PgpSignEncrypt when we have the signed hash!
data.putExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, pgpResult.getNfcTimestamp().getTime());
return getNfcSignIntent(data, passphrase, pgpResult.getNfcHash(), pgpResult.getNfcAlgo());
} else {
throw new PgpGeneralException(
"Encountered unhandled type of pending action not supported by API!");
}
}
if (!result.success()) {
LogEntryParcel errorMsg = result.getLog().getLast();
} else if (pgpResult.success()) {
// see end of method
} else {
LogEntryParcel errorMsg = pgpResult.getLog().getLast();
throw new Exception(getString(errorMsg.mType.getMsgId()));
}
@ -346,6 +361,17 @@ public class OpenPgpService extends RemoteService {
PgpSignEncrypt.Builder builder = new PgpSignEncrypt.Builder(
new ProviderHelper(getContext()),
new PassphraseCacheInterface() {
@Override
public String getCachedPassphrase(long masterKeyId) throws PassphraseCacheInterface.NoSecretKeyException {
try {
return PassphraseCacheService.getCachedPassphrase(
OpenPgpService.this, masterKeyId);
} catch (PassphraseCacheService.KeyNotFoundException e) {
throw new PassphraseCacheInterface.NoSecretKeyException();
}
}
},
inputData, os);
builder.setEnableAsciiArmorOutput(asciiArmor)
.setVersionHeader(PgpHelper.getVersionForHeader(this))
@ -384,24 +410,27 @@ public class OpenPgpService extends RemoteService {
}
// execute PGP operation!
SignEncryptResult result = builder.build().execute();
SignEncryptResult pgpResult = builder.build().execute();
if (result.isPending()) {
switch (result.getResult()) {
case SignEncryptResult.RESULT_PENDING_NFC:
// return PendingIntent to execute NFC activity
// pass through the signature creation timestamp to be used again on second execution
// of PgpSignEncrypt when we have the signed hash!
data.putExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, result.getNfcTimestamp().getTime());
return getNfcSignIntent(data, passphrase, result.getNfcHash(), result.getNfcAlgo());
default:
throw new Exception("Encountered unhandled pending state - please file a bug report!");
if (pgpResult.isPending()) {
if ((pgpResult.getResult() & SignEncryptResult.RESULT_PENDING_PASSPHRASE) ==
SignEncryptResult.RESULT_PENDING_PASSPHRASE) {
return getPassphraseIntent(data, pgpResult.getKeyIdPassphraseNeeded());
} else if ((pgpResult.getResult() & SignEncryptResult.RESULT_PENDING_NFC) ==
SignEncryptResult.RESULT_PENDING_NFC) {
// return PendingIntent to execute NFC activity
// pass through the signature creation timestamp to be used again on second execution
// of PgpSignEncrypt when we have the signed hash!
data.putExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, pgpResult.getNfcTimestamp().getTime());
return getNfcSignIntent(data, passphrase, pgpResult.getNfcHash(), pgpResult.getNfcAlgo());
} else {
throw new PgpGeneralException(
"Encountered unhandled type of pending action not supported by API!");
}
}
if (!result.success()) {
LogEntryParcel errorMsg = result.getLog().getLast();
} else if (pgpResult.success()) {
// see end of method
} else {
LogEntryParcel errorMsg = pgpResult.getLog().getLast();
throw new Exception(getString(errorMsg.mType.getMsgId()));
}
@ -445,14 +474,14 @@ public class OpenPgpService extends RemoteService {
PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(
new ProviderHelper(this),
new PgpDecryptVerify.PassphraseCache() {
new PassphraseCacheInterface() {
@Override
public String getCachedPassphrase(long masterKeyId) throws PgpDecryptVerify.NoSecretKeyException {
public String getCachedPassphrase(long masterKeyId) throws PassphraseCacheInterface.NoSecretKeyException {
try {
return PassphraseCacheService.getCachedPassphrase(
OpenPgpService.this, masterKeyId);
} catch (PassphraseCacheService.KeyNotFoundException e) {
throw new PgpDecryptVerify.NoSecretKeyException();
throw new PassphraseCacheInterface.NoSecretKeyException();
}
}
},
@ -470,57 +499,64 @@ public class OpenPgpService extends RemoteService {
.setNfcState(nfcDecryptedSessionKey);
// TODO: currently does not support binary signed-only content
DecryptVerifyResult decryptVerifyResult = builder.build().execute();
DecryptVerifyResult pgpResult = builder.build().execute();
if (decryptVerifyResult.isPending()) {
switch (decryptVerifyResult.getResult()) {
case DecryptVerifyResult.RESULT_PENDING_ASYM_PASSPHRASE:
return getPassphraseIntent(data, decryptVerifyResult.getKeyIdPassphraseNeeded());
case DecryptVerifyResult.RESULT_PENDING_SYM_PASSPHRASE:
throw new PgpGeneralException(
"Decryption of symmetric content not supported by API!");
case DecryptVerifyResult.RESULT_PENDING_NFC:
// TODO get passphrase here? currently not in DecryptVerifyResult
return getNfcDecryptIntent(
data, null, decryptVerifyResult.getNfcEncryptedSessionKey());
if (pgpResult.isPending()) {
if ((pgpResult.getResult() & DecryptVerifyResult.RESULT_PENDING_ASYM_PASSPHRASE) ==
DecryptVerifyResult.RESULT_PENDING_ASYM_PASSPHRASE) {
return getPassphraseIntent(data, pgpResult.getKeyIdPassphraseNeeded());
} else if ((pgpResult.getResult() & DecryptVerifyResult.RESULT_PENDING_SYM_PASSPHRASE) ==
DecryptVerifyResult.RESULT_PENDING_SYM_PASSPHRASE) {
throw new PgpGeneralException(
"Decryption of symmetric content not supported by API!");
} else if ((pgpResult.getResult() & DecryptVerifyResult.RESULT_PENDING_NFC) ==
DecryptVerifyResult.RESULT_PENDING_NFC) {
// TODO get passphrase here? currently not in DecryptVerifyResult
return getNfcDecryptIntent(
data, null, pgpResult.getNfcEncryptedSessionKey());
} else {
throw new PgpGeneralException(
"Encountered unhandled type of pending action not supported by API!");
}
throw new PgpGeneralException(
"Encountered unhandled type of pending action not supported by API!");
}
} else if (pgpResult.success()) {
OpenPgpSignatureResult signatureResult = decryptVerifyResult.getSignatureResult();
if (signatureResult != null) {
result.putExtra(OpenPgpApi.RESULT_SIGNATURE, signatureResult);
OpenPgpSignatureResult signatureResult = pgpResult.getSignatureResult();
if (signatureResult != null) {
result.putExtra(OpenPgpApi.RESULT_SIGNATURE, signatureResult);
if (data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) < 5) {
// SIGNATURE_KEY_REVOKED and SIGNATURE_KEY_EXPIRED have been added in version 5
if (signatureResult.getStatus() == OpenPgpSignatureResult.SIGNATURE_KEY_REVOKED
|| signatureResult.getStatus() == OpenPgpSignatureResult.SIGNATURE_KEY_EXPIRED) {
signatureResult.setStatus(OpenPgpSignatureResult.SIGNATURE_ERROR);
if (data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) < 5) {
// SIGNATURE_KEY_REVOKED and SIGNATURE_KEY_EXPIRED have been added in version 5
if (signatureResult.getStatus() == OpenPgpSignatureResult.SIGNATURE_KEY_REVOKED
|| signatureResult.getStatus() == OpenPgpSignatureResult.SIGNATURE_KEY_EXPIRED) {
signatureResult.setStatus(OpenPgpSignatureResult.SIGNATURE_ERROR);
}
}
if (signatureResult.getStatus() == OpenPgpSignatureResult.SIGNATURE_KEY_MISSING) {
// If signature is unknown we return an _additional_ PendingIntent
// to retrieve the missing key
Intent intent = new Intent(getBaseContext(), ImportKeysActivity.class);
intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_TO_SERVICE);
intent.putExtra(ImportKeysActivity.EXTRA_KEY_ID, signatureResult.getKeyId());
intent.putExtra(ImportKeysActivity.EXTRA_PENDING_INTENT_DATA, data);
PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0,
intent,
PendingIntent.FLAG_CANCEL_CURRENT);
result.putExtra(OpenPgpApi.RESULT_INTENT, pi);
}
}
if (signatureResult.getStatus() == OpenPgpSignatureResult.SIGNATURE_KEY_MISSING) {
// If signature is unknown we return an _additional_ PendingIntent
// to retrieve the missing key
Intent intent = new Intent(getBaseContext(), ImportKeysActivity.class);
intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_TO_SERVICE);
intent.putExtra(ImportKeysActivity.EXTRA_KEY_ID, signatureResult.getKeyId());
intent.putExtra(ImportKeysActivity.EXTRA_PENDING_INTENT_DATA, data);
PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0,
intent,
PendingIntent.FLAG_CANCEL_CURRENT);
result.putExtra(OpenPgpApi.RESULT_INTENT, pi);
}
}
if (data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) >= 4) {
OpenPgpMetadata metadata = decryptVerifyResult.getDecryptMetadata();
if (metadata != null) {
result.putExtra(OpenPgpApi.RESULT_METADATA, metadata);
if (data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) >= 4) {
OpenPgpMetadata metadata = pgpResult.getDecryptMetadata();
if (metadata != null) {
result.putExtra(OpenPgpApi.RESULT_METADATA, metadata);
}
}
} else {
LogEntryParcel errorMsg = pgpResult.getLog().getLast();
throw new Exception(getString(errorMsg.mType.getMsgId()));
}
} finally {
is.close();

View File

@ -40,6 +40,7 @@ import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing;
import org.sufficientlysecure.keychain.pgp.PassphraseCacheInterface;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
import org.sufficientlysecure.keychain.service.results.DecryptVerifyResult;
import org.sufficientlysecure.keychain.pgp.PgpHelper;
@ -269,6 +270,17 @@ public class KeychainIntentService extends IntentService implements Progressable
/* Operation */
PgpSignEncrypt.Builder builder = new PgpSignEncrypt.Builder(
new ProviderHelper(this),
new PassphraseCacheInterface() {
@Override
public String getCachedPassphrase(long masterKeyId) throws PassphraseCacheInterface.NoSecretKeyException {
try {
return PassphraseCacheService.getCachedPassphrase(
KeychainIntentService.this, masterKeyId);
} catch (PassphraseCacheService.KeyNotFoundException e) {
throw new PassphraseCacheInterface.NoSecretKeyException();
}
}
},
inputData, outStream
);
builder.setProgressable(this)
@ -342,14 +354,14 @@ public class KeychainIntentService extends IntentService implements Progressable
// verification of signatures
PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(
new ProviderHelper(this),
new PgpDecryptVerify.PassphraseCache() {
new PassphraseCacheInterface() {
@Override
public String getCachedPassphrase(long masterKeyId) {
public String getCachedPassphrase(long masterKeyId) throws PassphraseCacheInterface.NoSecretKeyException {
try {
return PassphraseCacheService.getCachedPassphrase(
KeychainIntentService.this, masterKeyId);
} catch (PassphraseCacheService.KeyNotFoundException e) {
return null;
throw new PassphraseCacheInterface.NoSecretKeyException();
}
}
},
@ -390,14 +402,14 @@ public class KeychainIntentService extends IntentService implements Progressable
// verification of signatures
PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(
new ProviderHelper(this),
new PgpDecryptVerify.PassphraseCache() {
new PassphraseCacheInterface() {
@Override
public String getCachedPassphrase(long masterKeyId) throws PgpDecryptVerify.NoSecretKeyException {
public String getCachedPassphrase(long masterKeyId) throws PassphraseCacheInterface.NoSecretKeyException {
try {
return PassphraseCacheService.getCachedPassphrase(
KeychainIntentService.this, masterKeyId);
} catch (PassphraseCacheService.KeyNotFoundException e) {
throw new PgpDecryptVerify.NoSecretKeyException();
throw new PassphraseCacheInterface.NoSecretKeyException();
}
}
},

View File

@ -26,12 +26,12 @@ import org.openintents.openpgp.OpenPgpSignatureResult;
public class DecryptVerifyResult extends OperationResult {
// the fourth bit indicates a "data pending" result! (it's also a form of non-success)
public static final int RESULT_PENDING = RESULT_ERROR +8;
public static final int RESULT_PENDING = RESULT_ERROR + 8;
// fifth to sixth bit in addition indicate specific type of pending
public static final int RESULT_PENDING_ASYM_PASSPHRASE = RESULT_PENDING +16;
public static final int RESULT_PENDING_SYM_PASSPHRASE = RESULT_PENDING +32;
public static final int RESULT_PENDING_NFC = RESULT_PENDING +48;
public static final int RESULT_PENDING_ASYM_PASSPHRASE = RESULT_PENDING + 16;
public static final int RESULT_PENDING_SYM_PASSPHRASE = RESULT_PENDING + 32;
public static final int RESULT_PENDING_NFC = RESULT_PENDING + 48;
long mKeyIdPassphraseNeeded;
byte[] mNfcSessionKey;

View File

@ -475,7 +475,6 @@ public abstract class OperationResult implements Parcelable {
MSG_SE_ERROR_SIGN_KEY(LogLevel.ERROR, R.string.msg_se_error_sign_key),
MSG_SE_ERROR_KEY_SIGN (LogLevel.ERROR, R.string.msg_se_error_key_sign),
MSG_SE_ERROR_NFC (LogLevel.ERROR, R.string.msg_se_error_nfc),
MSG_SE_ERROR_NO_PASSPHRASE (LogLevel.ERROR, R.string.msg_se_error_no_passphrase),
MSG_SE_ERROR_PGP (LogLevel.ERROR, R.string.msg_se_error_pgp),
MSG_SE_ERROR_SIG (LogLevel.ERROR, R.string.msg_se_error_sig),
MSG_SE_ERROR_UNLOCK (LogLevel.ERROR, R.string.msg_se_error_unlock),
@ -484,6 +483,7 @@ public abstract class OperationResult implements Parcelable {
MSG_SE_KEY_WARN (LogLevel.WARN, R.string.msg_se_key_warn),
MSG_SE_OK (LogLevel.OK, R.string.msg_se_ok),
MSG_SE_PENDING_NFC (LogLevel.INFO, R.string.msg_se_pending_nfc),
MSG_SE_PENDING_PASSPHRASE (LogLevel.INFO, R.string.msg_se_pending_passphrase),
MSG_SE (LogLevel.DEBUG, R.string.msg_se),
MSG_SE_SIGNING (LogLevel.DEBUG, R.string.msg_se_signing),
MSG_SE_SIGCRYPTING (LogLevel.DEBUG, R.string.msg_se_sigcrypting),

View File

@ -24,15 +24,26 @@ import java.util.Date;
public class SignEncryptResult extends OperationResult {
// the fourth bit indicates a "data pending" result! (it's also a form of non-success)
public static final int RESULT_PENDING = RESULT_ERROR +8;
public static final int RESULT_PENDING = RESULT_ERROR + 8;
// fifth to sixth bit in addition indicate specific type of pending
public static final int RESULT_PENDING_NFC = RESULT_PENDING +16;
public static final int RESULT_PENDING_PASSPHRASE = RESULT_PENDING + 16;
public static final int RESULT_PENDING_NFC = RESULT_PENDING + 32;
long mKeyIdPassphraseNeeded;
byte[] mNfcHash;
int mNfcAlgo;
Date mNfcTimestamp;
public long getKeyIdPassphraseNeeded() {
return mKeyIdPassphraseNeeded;
}
public void setKeyIdPassphraseNeeded(long keyIdPassphraseNeeded) {
mKeyIdPassphraseNeeded = keyIdPassphraseNeeded;
}
public void setNfcData(byte[] sessionKey, int nfcAlgo, Date nfcTimestamp) {
mNfcHash = sessionKey;
mNfcAlgo = nfcAlgo;
@ -52,7 +63,7 @@ public class SignEncryptResult extends OperationResult {
}
public boolean isPending() {
return (mResult & RESULT_PENDING) != 0;
return (mResult & RESULT_PENDING) == RESULT_PENDING;
}
public SignEncryptResult(int result, OperationLog log) {

View File

@ -22,7 +22,6 @@ import android.app.ProgressDialog;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Messenger;
import android.support.v4.app.Fragment;
@ -35,15 +34,11 @@ import org.sufficientlysecure.keychain.api.OpenKeychainIntents;
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
import org.sufficientlysecure.keychain.helper.Preferences;
import org.sufficientlysecure.keychain.helper.ShareHelper;
import org.sufficientlysecure.keychain.nfc.NfcActivity;
import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.service.results.SignEncryptResult;
import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Notify;
@ -200,22 +195,36 @@ public class EncryptTextActivity extends DrawerActivity implements EncryptActivi
SignEncryptResult result =
message.getData().getParcelable(SignEncryptResult.EXTRA_RESULT);
// TODO if (result.isPending())
if (result.isPending()) {
Log.d(Constants.TAG, "result.getResult() " + result.getResult());
if ((result.getResult() & SignEncryptResult.RESULT_PENDING_PASSPHRASE) ==
SignEncryptResult.RESULT_PENDING_PASSPHRASE) {
Log.d(Constants.TAG, "passp");
startPassphraseDialog(result.getKeyIdPassphraseNeeded());
} else if ((result.getResult() & SignEncryptResult.RESULT_PENDING_NFC) ==
SignEncryptResult.RESULT_PENDING_NFC) {
Log.d(Constants.TAG, "nfc");
if (!result.success()) {
result.createNotify(EncryptTextActivity.this).show();
return;
}
// use after nfc sign
//// data.putExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, result.getNfcTimestamp().getTime());
startNfcSign("123456", result.getNfcHash(), result.getNfcAlgo());
} else {
throw new RuntimeException("Unhandled pending result!");
}
if (mShareAfterEncrypt) {
// Share encrypted message/file
startActivity(sendWithChooserExcludingEncrypt(message));
} else if (result.success()) {
if (mShareAfterEncrypt) {
// Share encrypted message/file
startActivity(sendWithChooserExcludingEncrypt(message));
} else {
// Copy to clipboard
copyToClipboard(message);
result.createNotify(EncryptTextActivity.this).show();
// Notify.showNotify(EncryptTextActivity.this,
// R.string.encrypt_sign_clipboard_successful, Notify.Style.INFO);
}
} else {
// Copy to clipboard
copyToClipboard(message);
result.createNotify(EncryptTextActivity.this).show();
// Notify.showNotify(EncryptTextActivity.this,
// R.string.encrypt_sign_clipboard_successful, Notify.Style.INFO);
}
}
}
@ -231,6 +240,36 @@ public class EncryptTextActivity extends DrawerActivity implements EncryptActivi
startService(intent);
}
private void startNfcSign(String pin, byte[] hashToSign, int hashAlgo) {
Intent data = new Intent();
// build PendingIntent for Yubikey NFC operations
Intent intent = new Intent(this, NfcActivity.class);
intent.setAction(NfcActivity.ACTION_SIGN_HASH);
// pass params through to activity that it can be returned again later to repeat pgp operation
intent.putExtra(NfcActivity.EXTRA_DATA, data);
intent.putExtra(NfcActivity.EXTRA_PIN, pin);
intent.putExtra(NfcActivity.EXTRA_NFC_HASH_TO_SIGN, hashToSign);
intent.putExtra(NfcActivity.EXTRA_NFC_HASH_ALGO, hashAlgo);
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivityForResult(intent, 0);
}
private void startPassphraseDialog(long subkeyId) {
Intent data = new Intent();
// build PendingIntent for Yubikey NFC operations
Intent intent = new Intent(this, PassphraseDialogActivity.class);
// pass params through to activity that it can be returned again later to repeat pgp operation
intent.putExtra(PassphraseDialogActivity.EXTRA_SUBKEY_ID, subkeyId);
// intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivityForResult(intent, 0);
}
private Bundle createEncryptBundle() {
// fill values for this action
Bundle data = new Bundle();
@ -326,35 +365,35 @@ public class EncryptTextActivity extends DrawerActivity implements EncryptActivi
return false;
}
try {
// TODO This should really not be decided here. We do need the info for the passphrase
// TODO dialog fragment though, so that's just the way it is for now.
if (mSigningKeyId != 0) {
CachedPublicKeyRing signingRing =
new ProviderHelper(this).getCachedPublicKeyRing(mSigningKeyId);
long sigSubKeyId = signingRing.getSignId();
// Make sure the passphrase is cached, then start over.
if (PassphraseCacheService.getCachedPassphrase(this, sigSubKeyId) == null) {
PassphraseDialogFragment.show(this, sigSubKeyId,
new Handler() {
@Override
public void handleMessage(Message message) {
if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
// restart
startEncrypt();
}
}
}
);
return false;
}
}
} catch (PgpGeneralException e) {
Log.e(Constants.TAG, "Key not found!", e);
} catch (PassphraseCacheService.KeyNotFoundException e) {
Log.e(Constants.TAG, "Key not found!", e);
}
// try {
// // TODO This should really not be decided here. We do need the info for the passphrase
// // TODO dialog fragment though, so that's just the way it is for now.
// if (mSigningKeyId != 0) {
// CachedPublicKeyRing signingRing =
// new ProviderHelper(this).getCachedPublicKeyRing(mSigningKeyId);
// long sigSubKeyId = signingRing.getSignId();
// // Make sure the passphrase is cached, then start over.
// if (PassphraseCacheService.getCachedPassphrase(this, sigSubKeyId) == null) {
// PassphraseDialogFragment.show(this, sigSubKeyId,
// new Handler() {
// @Override
// public void handleMessage(Message message) {
// if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
// // restart
// startEncrypt();
// }
// }
// }
// );
//
// return false;
// }
// }
// } catch (PgpGeneralException e) {
// Log.e(Constants.TAG, "Key not found!", e);
// } catch (PassphraseCacheService.KeyNotFoundException e) {
// Log.e(Constants.TAG, "Key not found!", e);
// }
}
return true;
}

View File

@ -32,13 +32,12 @@ import org.sufficientlysecure.keychain.R;
public class EncryptTextFragment extends Fragment {
public static final String ARG_TEXT = "text";
private TextView mMessage = null;
private TextView mText;
private View mEncryptShare;
private View mEncryptClipboard;
private EncryptActivityInterface mEncryptInterface;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
@ -56,8 +55,8 @@ public class EncryptTextFragment extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.encrypt_text_fragment, container, false);
mMessage = (TextView) view.findViewById(R.id.message);
mMessage.addTextChangedListener(new TextWatcher() {
mText = (TextView) view.findViewById(R.id.encrypt_text_text);
mText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
@ -98,7 +97,7 @@ public class EncryptTextFragment extends Fragment {
String text = mEncryptInterface.getMessage();
if (text != null) {
mMessage.setText(text);
mText.setText(text);
}
}
}

View File

@ -59,7 +59,7 @@ import org.sufficientlysecure.keychain.util.Log;
public class PassphraseDialogActivity extends FragmentActivity {
public static final String MESSAGE_DATA_PASSPHRASE = "passphrase";
public static final String EXTRA_SECRET_KEY_ID = "secret_key_id";
public static final String EXTRA_SUBKEY_ID = "secret_key_id";
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -76,7 +76,7 @@ public class PassphraseDialogActivity extends FragmentActivity {
// this activity itself has no content view (see manifest)
long keyId = getIntent().getLongExtra(EXTRA_SECRET_KEY_ID, 0);
long keyId = getIntent().getLongExtra(EXTRA_SUBKEY_ID, 0);
show(this, keyId);
}
@ -92,7 +92,7 @@ public class PassphraseDialogActivity extends FragmentActivity {
// do NOT check if the key even needs a passphrase. that's not our job here.
PassphraseDialogFragment frag = new PassphraseDialogFragment();
Bundle args = new Bundle();
args.putLong(EXTRA_SECRET_KEY_ID, keyId);
args.putLong(EXTRA_SUBKEY_ID, keyId);
frag.setArguments(args);
@ -116,7 +116,7 @@ public class PassphraseDialogActivity extends FragmentActivity {
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Activity activity = getActivity();
mSubKeyId = getArguments().getLong(EXTRA_SECRET_KEY_ID);
mSubKeyId = getArguments().getLong(EXTRA_SUBKEY_ID);
CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(activity);

View File

@ -2,12 +2,14 @@ package org.sufficientlysecure.keychain.ui.dialog;
import android.app.Activity;
import android.app.AlertDialog;
import android.view.ContextThemeWrapper;
import android.view.View;
import android.widget.TextView;
import org.sufficientlysecure.keychain.R;
/** This class extends AlertDiaog.Builder, styling the header using emphasis color.
/**
* This class extends AlertDiaog.Builder, styling the header using emphasis color.
* Note that this class is a huge hack, because dialog boxes aren't easily stylable.
* Also, the dialog NEEDS to be called with show() directly, not create(), otherwise
* the order of internal operations will lead to a crash!
@ -15,7 +17,9 @@ import org.sufficientlysecure.keychain.R;
public class CustomAlertDialogBuilder extends AlertDialog.Builder {
public CustomAlertDialogBuilder(Activity activity) {
super(activity);
// if the progress 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
super(new ContextThemeWrapper(activity, R.style.Theme_AppCompat_Light));
}
@Override

View File

@ -13,7 +13,7 @@
android:orientation="vertical">
<EditText
android:id="@+id/message"
android:id="@+id/encrypt_text_text"
android:layout_width="match_parent"
android:layout_height="0dip"
android:gravity="top"

View File

@ -823,6 +823,7 @@
<string name="msg_se_key_warn">"Bad key for encryption: %s"</string>
<string name="msg_se_ok">"Sign/Encrypt operation successful!"</string>
<string name="msg_se_pending_nfc">"NFC token required, requesting user input…"</string>
<string name="msg_se_pending_passphrase">"Passphrase required, requesting user input…"</string>
<string name="msg_se_signing">"Signing data (without encryption)"</string>
<string name="msg_se_sigcrypting">"Encrypting data with signature"</string>
<string name="msg_se">"Starting sign and/or encrypt operation"</string>