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 { public class PgpDecryptVerify {
private ProviderHelper mProviderHelper; private ProviderHelper mProviderHelper;
private PassphraseCache mPassphraseCache; private PassphraseCacheInterface mPassphraseCache;
private InputData mData; private InputData mData;
private OutputStream mOutStream; private OutputStream mOutStream;
@ -101,7 +101,7 @@ public class PgpDecryptVerify {
public static class Builder { public static class Builder {
// mandatory parameter // mandatory parameter
private ProviderHelper mProviderHelper; private ProviderHelper mProviderHelper;
private PassphraseCache mPassphraseCache; private PassphraseCacheInterface mPassphraseCache;
private InputData mData; private InputData mData;
private OutputStream mOutStream; private OutputStream mOutStream;
@ -113,12 +113,12 @@ public class PgpDecryptVerify {
private boolean mDecryptMetadataOnly = false; private boolean mDecryptMetadataOnly = false;
private byte[] mDecryptedSessionKey = null; private byte[] mDecryptedSessionKey = null;
public Builder(ProviderHelper providerHelper, PassphraseCache passphraseCache, public Builder(ProviderHelper providerHelper, PassphraseCacheInterface passphraseCache,
InputData data, OutputStream outStream) { InputData data, OutputStream outStream) {
this.mProviderHelper = providerHelper; mProviderHelper = providerHelper;
this.mPassphraseCache = passphraseCache; mPassphraseCache = passphraseCache;
this.mData = data; mData = data;
this.mOutStream = outStream; mOutStream = outStream;
} }
public Builder setProgressable(Progressable progressable) { 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 * Decrypts and/or verifies data based on parameters of class
*/ */
@ -322,7 +312,7 @@ public class PgpDecryptVerify {
// returns "" if key has no passphrase // returns "" if key has no passphrase
mPassphrase = mPassphraseCache.getCachedPassphrase(subKeyId); mPassphrase = mPassphraseCache.getCachedPassphrase(subKeyId);
log.add(LogType.MSG_DC_PASS_CACHED, indent +1); 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); log.add(LogType.MSG_DC_ERROR_NO_KEY, indent +1);
return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
} }

View File

@ -59,6 +59,7 @@ import java.util.LinkedList;
*/ */
public class PgpSignEncrypt { public class PgpSignEncrypt {
private ProviderHelper mProviderHelper; private ProviderHelper mProviderHelper;
private PassphraseCacheInterface mPassphraseCache;
private String mVersionHeader; private String mVersionHeader;
private InputData mData; private InputData mData;
private OutputStream mOutStream; private OutputStream mOutStream;
@ -93,6 +94,7 @@ public class PgpSignEncrypt {
private PgpSignEncrypt(Builder builder) { private PgpSignEncrypt(Builder builder) {
// private Constructor can only be called from Builder // private Constructor can only be called from Builder
this.mProviderHelper = builder.mProviderHelper; this.mProviderHelper = builder.mProviderHelper;
this.mPassphraseCache = builder.mPassphraseCache;
this.mVersionHeader = builder.mVersionHeader; this.mVersionHeader = builder.mVersionHeader;
this.mData = builder.mData; this.mData = builder.mData;
this.mOutStream = builder.mOutStream; this.mOutStream = builder.mOutStream;
@ -117,6 +119,7 @@ public class PgpSignEncrypt {
public static class Builder { public static class Builder {
// mandatory parameter // mandatory parameter
private ProviderHelper mProviderHelper; private ProviderHelper mProviderHelper;
private PassphraseCacheInterface mPassphraseCache;
private InputData mData; private InputData mData;
private OutputStream mOutStream; private OutputStream mOutStream;
@ -138,8 +141,10 @@ public class PgpSignEncrypt {
private byte[] mNfcSignedHash = null; private byte[] mNfcSignedHash = null;
private Date mNfcCreationTimestamp = 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; mProviderHelper = providerHelper;
mPassphraseCache = passphraseCache;
mData = data; mData = data;
mOutStream = outStream; mOutStream = outStream;
} }
@ -290,20 +295,15 @@ public class PgpSignEncrypt {
/* Get keys for signature generation for later usage */ /* Get keys for signature generation for later usage */
CanonicalizedSecretKey signingKey = null; CanonicalizedSecretKey signingKey = null;
long signKeyId;
if (enableSignature) { 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 { try {
// fetch the indicated master key id (the one whose name we sign in) // fetch the indicated master key id (the one whose name we sign in)
CanonicalizedSecretKeyRing signingKeyRing = CanonicalizedSecretKeyRing signingKeyRing =
mProviderHelper.getCanonicalizedSecretKeyRing(mSignatureMasterKeyId); mProviderHelper.getCanonicalizedSecretKeyRing(mSignatureMasterKeyId);
// fetch the specific subkey to sign with, or just use the master key if none specified // 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); signingKey = signingKeyRing.getSecretKey(signKeyId);
// make sure it's a signing key alright! // make sure it's a signing key alright!
} catch (ProviderHelper.NotFoundException e) { } catch (ProviderHelper.NotFoundException e) {
@ -317,6 +317,28 @@ public class PgpSignEncrypt {
return new SignEncryptResult(SignEncryptResult.RESULT_ERROR, log); 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); updateProgress(R.string.progress_extracting_signature_key, 0, 100);
try { try {
@ -369,10 +391,10 @@ public class PgpSignEncrypt {
log.add(LogType.MSG_SE_KEY_OK, indent + 1, log.add(LogType.MSG_SE_KEY_OK, indent + 1,
PgpKeyHelper.convertKeyIdToHex(id)); PgpKeyHelper.convertKeyIdToHex(id));
} catch (PgpGeneralException e) { } catch (PgpGeneralException e) {
log.add(LogType.MSG_SE_KEY_WARN, indent +1, log.add(LogType.MSG_SE_KEY_WARN, indent + 1,
PgpKeyHelper.convertKeyIdToHex(id)); PgpKeyHelper.convertKeyIdToHex(id));
} catch (ProviderHelper.NotFoundException e) { } 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)); PgpKeyHelper.convertKeyIdToHex(id));
} }
} }
@ -409,7 +431,8 @@ public class PgpSignEncrypt {
log.add(enableSignature log.add(enableSignature
? LogType.MSG_SE_SIGCRYPTING ? LogType.MSG_SE_SIGCRYPTING
: LogType.MSG_SE_ENCRYPTING, : LogType.MSG_SE_ENCRYPTING,
indent); indent
);
indent += 1; indent += 1;
encryptionOut = cPk.open(out, new byte[1 << 16]); 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.openintents.openpgp.util.OpenPgpApi;
import org.sufficientlysecure.keychain.nfc.NfcActivity; import org.sufficientlysecure.keychain.nfc.NfcActivity;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.PassphraseCacheInterface;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify; import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables; import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
import org.sufficientlysecure.keychain.service.results.DecryptVerifyResult; import org.sufficientlysecure.keychain.service.results.DecryptVerifyResult;
@ -259,6 +260,17 @@ public class OpenPgpService extends RemoteService {
// sign-only // sign-only
PgpSignEncrypt.Builder builder = new PgpSignEncrypt.Builder( PgpSignEncrypt.Builder builder = new PgpSignEncrypt.Builder(
new ProviderHelper(getContext()), 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); inputData, os);
builder.setEnableAsciiArmorOutput(asciiArmor) builder.setEnableAsciiArmorOutput(asciiArmor)
.setVersionHeader(PgpHelper.getVersionForHeader(this)) .setVersionHeader(PgpHelper.getVersionForHeader(this))
@ -271,24 +283,27 @@ public class OpenPgpService extends RemoteService {
builder.setCleartextInput(true); builder.setCleartextInput(true);
// execute PGP operation! // execute PGP operation!
SignEncryptResult result = builder.build().execute(); SignEncryptResult pgpResult = builder.build().execute();
if (result.isPending()) { if (pgpResult.isPending()) {
switch (result.getResult()) { if ((pgpResult.getResult() & SignEncryptResult.RESULT_PENDING_PASSPHRASE) ==
case SignEncryptResult.RESULT_PENDING_NFC: 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 // return PendingIntent to execute NFC activity
// pass through the signature creation timestamp to be used again on second execution // pass through the signature creation timestamp to be used again on second execution
// of PgpSignEncrypt when we have the signed hash! // of PgpSignEncrypt when we have the signed hash!
data.putExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, result.getNfcTimestamp().getTime()); data.putExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, pgpResult.getNfcTimestamp().getTime());
return getNfcSignIntent(data, passphrase, result.getNfcHash(), result.getNfcAlgo()); return getNfcSignIntent(data, passphrase, pgpResult.getNfcHash(), pgpResult.getNfcAlgo());
} else {
default: throw new PgpGeneralException(
throw new Exception("Encountered unhandled pending state - please file a bug report!"); "Encountered unhandled type of pending action not supported by API!");
} }
} } else if (pgpResult.success()) {
// see end of method
if (!result.success()) { } else {
LogEntryParcel errorMsg = result.getLog().getLast(); LogEntryParcel errorMsg = pgpResult.getLog().getLast();
throw new Exception(getString(errorMsg.mType.getMsgId())); throw new Exception(getString(errorMsg.mType.getMsgId()));
} }
@ -346,6 +361,17 @@ public class OpenPgpService extends RemoteService {
PgpSignEncrypt.Builder builder = new PgpSignEncrypt.Builder( PgpSignEncrypt.Builder builder = new PgpSignEncrypt.Builder(
new ProviderHelper(getContext()), 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); inputData, os);
builder.setEnableAsciiArmorOutput(asciiArmor) builder.setEnableAsciiArmorOutput(asciiArmor)
.setVersionHeader(PgpHelper.getVersionForHeader(this)) .setVersionHeader(PgpHelper.getVersionForHeader(this))
@ -384,24 +410,27 @@ public class OpenPgpService extends RemoteService {
} }
// execute PGP operation! // execute PGP operation!
SignEncryptResult result = builder.build().execute(); SignEncryptResult pgpResult = builder.build().execute();
if (result.isPending()) { if (pgpResult.isPending()) {
switch (result.getResult()) { if ((pgpResult.getResult() & SignEncryptResult.RESULT_PENDING_PASSPHRASE) ==
case SignEncryptResult.RESULT_PENDING_NFC: 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 // return PendingIntent to execute NFC activity
// pass through the signature creation timestamp to be used again on second execution // pass through the signature creation timestamp to be used again on second execution
// of PgpSignEncrypt when we have the signed hash! // of PgpSignEncrypt when we have the signed hash!
data.putExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, result.getNfcTimestamp().getTime()); data.putExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, pgpResult.getNfcTimestamp().getTime());
return getNfcSignIntent(data, passphrase, result.getNfcHash(), result.getNfcAlgo()); return getNfcSignIntent(data, passphrase, pgpResult.getNfcHash(), pgpResult.getNfcAlgo());
} else {
default: throw new PgpGeneralException(
throw new Exception("Encountered unhandled pending state - please file a bug report!"); "Encountered unhandled type of pending action not supported by API!");
} }
} } else if (pgpResult.success()) {
// see end of method
if (!result.success()) { } else {
LogEntryParcel errorMsg = result.getLog().getLast(); LogEntryParcel errorMsg = pgpResult.getLog().getLast();
throw new Exception(getString(errorMsg.mType.getMsgId())); throw new Exception(getString(errorMsg.mType.getMsgId()));
} }
@ -445,14 +474,14 @@ public class OpenPgpService extends RemoteService {
PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder( PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(
new ProviderHelper(this), new ProviderHelper(this),
new PgpDecryptVerify.PassphraseCache() { new PassphraseCacheInterface() {
@Override @Override
public String getCachedPassphrase(long masterKeyId) throws PgpDecryptVerify.NoSecretKeyException { public String getCachedPassphrase(long masterKeyId) throws PassphraseCacheInterface.NoSecretKeyException {
try { try {
return PassphraseCacheService.getCachedPassphrase( return PassphraseCacheService.getCachedPassphrase(
OpenPgpService.this, masterKeyId); OpenPgpService.this, masterKeyId);
} catch (PassphraseCacheService.KeyNotFoundException e) { } catch (PassphraseCacheService.KeyNotFoundException e) {
throw new PgpDecryptVerify.NoSecretKeyException(); throw new PassphraseCacheInterface.NoSecretKeyException();
} }
} }
}, },
@ -470,25 +499,28 @@ public class OpenPgpService extends RemoteService {
.setNfcState(nfcDecryptedSessionKey); .setNfcState(nfcDecryptedSessionKey);
// TODO: currently does not support binary signed-only content // TODO: currently does not support binary signed-only content
DecryptVerifyResult decryptVerifyResult = builder.build().execute(); DecryptVerifyResult pgpResult = builder.build().execute();
if (decryptVerifyResult.isPending()) { if (pgpResult.isPending()) {
switch (decryptVerifyResult.getResult()) { if ((pgpResult.getResult() & DecryptVerifyResult.RESULT_PENDING_ASYM_PASSPHRASE) ==
case DecryptVerifyResult.RESULT_PENDING_ASYM_PASSPHRASE: DecryptVerifyResult.RESULT_PENDING_ASYM_PASSPHRASE) {
return getPassphraseIntent(data, decryptVerifyResult.getKeyIdPassphraseNeeded()); return getPassphraseIntent(data, pgpResult.getKeyIdPassphraseNeeded());
case DecryptVerifyResult.RESULT_PENDING_SYM_PASSPHRASE: } else if ((pgpResult.getResult() & DecryptVerifyResult.RESULT_PENDING_SYM_PASSPHRASE) ==
DecryptVerifyResult.RESULT_PENDING_SYM_PASSPHRASE) {
throw new PgpGeneralException( throw new PgpGeneralException(
"Decryption of symmetric content not supported by API!"); "Decryption of symmetric content not supported by API!");
case DecryptVerifyResult.RESULT_PENDING_NFC: } else if ((pgpResult.getResult() & DecryptVerifyResult.RESULT_PENDING_NFC) ==
DecryptVerifyResult.RESULT_PENDING_NFC) {
// TODO get passphrase here? currently not in DecryptVerifyResult // TODO get passphrase here? currently not in DecryptVerifyResult
return getNfcDecryptIntent( return getNfcDecryptIntent(
data, null, decryptVerifyResult.getNfcEncryptedSessionKey()); data, null, pgpResult.getNfcEncryptedSessionKey());
} } else {
throw new PgpGeneralException( throw new PgpGeneralException(
"Encountered unhandled type of pending action not supported by API!"); "Encountered unhandled type of pending action not supported by API!");
} }
} else if (pgpResult.success()) {
OpenPgpSignatureResult signatureResult = decryptVerifyResult.getSignatureResult(); OpenPgpSignatureResult signatureResult = pgpResult.getSignatureResult();
if (signatureResult != null) { if (signatureResult != null) {
result.putExtra(OpenPgpApi.RESULT_SIGNATURE, signatureResult); result.putExtra(OpenPgpApi.RESULT_SIGNATURE, signatureResult);
@ -517,11 +549,15 @@ public class OpenPgpService extends RemoteService {
} }
if (data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) >= 4) { if (data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) >= 4) {
OpenPgpMetadata metadata = decryptVerifyResult.getDecryptMetadata(); OpenPgpMetadata metadata = pgpResult.getDecryptMetadata();
if (metadata != null) { if (metadata != null) {
result.putExtra(OpenPgpApi.RESULT_METADATA, metadata); result.putExtra(OpenPgpApi.RESULT_METADATA, metadata);
} }
} }
} else {
LogEntryParcel errorMsg = pgpResult.getLog().getLast();
throw new Exception(getString(errorMsg.mType.getMsgId()));
}
} finally { } finally {
is.close(); is.close();
if (os != null) { if (os != null) {

View File

@ -40,6 +40,7 @@ import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing; import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey; import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing; import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing;
import org.sufficientlysecure.keychain.pgp.PassphraseCacheInterface;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify; import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
import org.sufficientlysecure.keychain.service.results.DecryptVerifyResult; import org.sufficientlysecure.keychain.service.results.DecryptVerifyResult;
import org.sufficientlysecure.keychain.pgp.PgpHelper; import org.sufficientlysecure.keychain.pgp.PgpHelper;
@ -269,6 +270,17 @@ public class KeychainIntentService extends IntentService implements Progressable
/* Operation */ /* Operation */
PgpSignEncrypt.Builder builder = new PgpSignEncrypt.Builder( PgpSignEncrypt.Builder builder = new PgpSignEncrypt.Builder(
new ProviderHelper(this), 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 inputData, outStream
); );
builder.setProgressable(this) builder.setProgressable(this)
@ -342,14 +354,14 @@ public class KeychainIntentService extends IntentService implements Progressable
// verification of signatures // verification of signatures
PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder( PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(
new ProviderHelper(this), new ProviderHelper(this),
new PgpDecryptVerify.PassphraseCache() { new PassphraseCacheInterface() {
@Override @Override
public String getCachedPassphrase(long masterKeyId) { public String getCachedPassphrase(long masterKeyId) throws PassphraseCacheInterface.NoSecretKeyException {
try { try {
return PassphraseCacheService.getCachedPassphrase( return PassphraseCacheService.getCachedPassphrase(
KeychainIntentService.this, masterKeyId); KeychainIntentService.this, masterKeyId);
} catch (PassphraseCacheService.KeyNotFoundException e) { } catch (PassphraseCacheService.KeyNotFoundException e) {
return null; throw new PassphraseCacheInterface.NoSecretKeyException();
} }
} }
}, },
@ -390,14 +402,14 @@ public class KeychainIntentService extends IntentService implements Progressable
// verification of signatures // verification of signatures
PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder( PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(
new ProviderHelper(this), new ProviderHelper(this),
new PgpDecryptVerify.PassphraseCache() { new PassphraseCacheInterface() {
@Override @Override
public String getCachedPassphrase(long masterKeyId) throws PgpDecryptVerify.NoSecretKeyException { public String getCachedPassphrase(long masterKeyId) throws PassphraseCacheInterface.NoSecretKeyException {
try { try {
return PassphraseCacheService.getCachedPassphrase( return PassphraseCacheService.getCachedPassphrase(
KeychainIntentService.this, masterKeyId); KeychainIntentService.this, masterKeyId);
} catch (PassphraseCacheService.KeyNotFoundException e) { } 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 { public class DecryptVerifyResult extends OperationResult {
// the fourth bit indicates a "data pending" result! (it's also a form of non-success) // 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 // 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_ASYM_PASSPHRASE = RESULT_PENDING + 16;
public static final int RESULT_PENDING_SYM_PASSPHRASE = RESULT_PENDING +32; 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_NFC = RESULT_PENDING + 48;
long mKeyIdPassphraseNeeded; long mKeyIdPassphraseNeeded;
byte[] mNfcSessionKey; 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_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_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_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_PGP (LogLevel.ERROR, R.string.msg_se_error_pgp),
MSG_SE_ERROR_SIG (LogLevel.ERROR, R.string.msg_se_error_sig), MSG_SE_ERROR_SIG (LogLevel.ERROR, R.string.msg_se_error_sig),
MSG_SE_ERROR_UNLOCK (LogLevel.ERROR, R.string.msg_se_error_unlock), 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_KEY_WARN (LogLevel.WARN, R.string.msg_se_key_warn),
MSG_SE_OK (LogLevel.OK, R.string.msg_se_ok), 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_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 (LogLevel.DEBUG, R.string.msg_se),
MSG_SE_SIGNING (LogLevel.DEBUG, R.string.msg_se_signing), MSG_SE_SIGNING (LogLevel.DEBUG, R.string.msg_se_signing),
MSG_SE_SIGCRYPTING (LogLevel.DEBUG, R.string.msg_se_sigcrypting), 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 { public class SignEncryptResult extends OperationResult {
// the fourth bit indicates a "data pending" result! (it's also a form of non-success) // 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 // 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; byte[] mNfcHash;
int mNfcAlgo; int mNfcAlgo;
Date mNfcTimestamp; Date mNfcTimestamp;
public long getKeyIdPassphraseNeeded() {
return mKeyIdPassphraseNeeded;
}
public void setKeyIdPassphraseNeeded(long keyIdPassphraseNeeded) {
mKeyIdPassphraseNeeded = keyIdPassphraseNeeded;
}
public void setNfcData(byte[] sessionKey, int nfcAlgo, Date nfcTimestamp) { public void setNfcData(byte[] sessionKey, int nfcAlgo, Date nfcTimestamp) {
mNfcHash = sessionKey; mNfcHash = sessionKey;
mNfcAlgo = nfcAlgo; mNfcAlgo = nfcAlgo;
@ -52,7 +63,7 @@ public class SignEncryptResult extends OperationResult {
} }
public boolean isPending() { public boolean isPending() {
return (mResult & RESULT_PENDING) != 0; return (mResult & RESULT_PENDING) == RESULT_PENDING;
} }
public SignEncryptResult(int result, OperationLog log) { public SignEncryptResult(int result, OperationLog log) {

View File

@ -22,7 +22,6 @@ import android.app.ProgressDialog;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.os.Message; import android.os.Message;
import android.os.Messenger; import android.os.Messenger;
import android.support.v4.app.Fragment; 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.compatibility.ClipboardReflection;
import org.sufficientlysecure.keychain.helper.Preferences; import org.sufficientlysecure.keychain.helper.Preferences;
import org.sufficientlysecure.keychain.helper.ShareHelper; import org.sufficientlysecure.keychain.helper.ShareHelper;
import org.sufficientlysecure.keychain.nfc.NfcActivity;
import org.sufficientlysecure.keychain.pgp.KeyRing; 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.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.service.results.SignEncryptResult; 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.Log;
import org.sufficientlysecure.keychain.util.Notify; import org.sufficientlysecure.keychain.util.Notify;
@ -200,13 +195,24 @@ public class EncryptTextActivity extends DrawerActivity implements EncryptActivi
SignEncryptResult result = SignEncryptResult result =
message.getData().getParcelable(SignEncryptResult.EXTRA_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()) { // use after nfc sign
result.createNotify(EncryptTextActivity.this).show(); //// data.putExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, result.getNfcTimestamp().getTime());
return; startNfcSign("123456", result.getNfcHash(), result.getNfcAlgo());
} else {
throw new RuntimeException("Unhandled pending result!");
} }
} else if (result.success()) {
if (mShareAfterEncrypt) { if (mShareAfterEncrypt) {
// Share encrypted message/file // Share encrypted message/file
startActivity(sendWithChooserExcludingEncrypt(message)); startActivity(sendWithChooserExcludingEncrypt(message));
@ -217,6 +223,9 @@ public class EncryptTextActivity extends DrawerActivity implements EncryptActivi
// Notify.showNotify(EncryptTextActivity.this, // Notify.showNotify(EncryptTextActivity.this,
// R.string.encrypt_sign_clipboard_successful, Notify.Style.INFO); // R.string.encrypt_sign_clipboard_successful, Notify.Style.INFO);
} }
} else {
result.createNotify(EncryptTextActivity.this).show();
}
} }
} }
}; };
@ -231,6 +240,36 @@ public class EncryptTextActivity extends DrawerActivity implements EncryptActivi
startService(intent); 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() { private Bundle createEncryptBundle() {
// fill values for this action // fill values for this action
Bundle data = new Bundle(); Bundle data = new Bundle();
@ -326,35 +365,35 @@ public class EncryptTextActivity extends DrawerActivity implements EncryptActivi
return false; return false;
} }
try { // try {
// TODO This should really not be decided here. We do need the info for the passphrase // // 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. // // TODO dialog fragment though, so that's just the way it is for now.
if (mSigningKeyId != 0) { // if (mSigningKeyId != 0) {
CachedPublicKeyRing signingRing = // CachedPublicKeyRing signingRing =
new ProviderHelper(this).getCachedPublicKeyRing(mSigningKeyId); // new ProviderHelper(this).getCachedPublicKeyRing(mSigningKeyId);
long sigSubKeyId = signingRing.getSignId(); // long sigSubKeyId = signingRing.getSignId();
// Make sure the passphrase is cached, then start over. // // Make sure the passphrase is cached, then start over.
if (PassphraseCacheService.getCachedPassphrase(this, sigSubKeyId) == null) { // if (PassphraseCacheService.getCachedPassphrase(this, sigSubKeyId) == null) {
PassphraseDialogFragment.show(this, sigSubKeyId, // PassphraseDialogFragment.show(this, sigSubKeyId,
new Handler() { // new Handler() {
@Override // @Override
public void handleMessage(Message message) { // public void handleMessage(Message message) {
if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) { // if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
// restart // // restart
startEncrypt(); // startEncrypt();
} // }
} // }
} // }
); // );
//
return false; // return false;
} // }
} // }
} catch (PgpGeneralException e) { // } catch (PgpGeneralException e) {
Log.e(Constants.TAG, "Key not found!", e); // Log.e(Constants.TAG, "Key not found!", e);
} catch (PassphraseCacheService.KeyNotFoundException e) { // } catch (PassphraseCacheService.KeyNotFoundException e) {
Log.e(Constants.TAG, "Key not found!", e); // Log.e(Constants.TAG, "Key not found!", e);
} // }
} }
return true; return true;
} }

View File

@ -32,13 +32,12 @@ import org.sufficientlysecure.keychain.R;
public class EncryptTextFragment extends Fragment { public class EncryptTextFragment extends Fragment {
public static final String ARG_TEXT = "text"; public static final String ARG_TEXT = "text";
private TextView mMessage = null; private TextView mText;
private View mEncryptShare; private View mEncryptShare;
private View mEncryptClipboard; private View mEncryptClipboard;
private EncryptActivityInterface mEncryptInterface; private EncryptActivityInterface mEncryptInterface;
@Override @Override
public void onAttach(Activity activity) { public void onAttach(Activity activity) {
super.onAttach(activity); super.onAttach(activity);
@ -56,8 +55,8 @@ public class EncryptTextFragment extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.encrypt_text_fragment, container, false); View view = inflater.inflate(R.layout.encrypt_text_fragment, container, false);
mMessage = (TextView) view.findViewById(R.id.message); mText = (TextView) view.findViewById(R.id.encrypt_text_text);
mMessage.addTextChangedListener(new TextWatcher() { mText.addTextChangedListener(new TextWatcher() {
@Override @Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) { public void beforeTextChanged(CharSequence s, int start, int count, int after) {
@ -98,7 +97,7 @@ public class EncryptTextFragment extends Fragment {
String text = mEncryptInterface.getMessage(); String text = mEncryptInterface.getMessage();
if (text != null) { 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 class PassphraseDialogActivity extends FragmentActivity {
public static final String MESSAGE_DATA_PASSPHRASE = "passphrase"; 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 @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
@ -76,7 +76,7 @@ public class PassphraseDialogActivity extends FragmentActivity {
// this activity itself has no content view (see manifest) // 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); 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. // do NOT check if the key even needs a passphrase. that's not our job here.
PassphraseDialogFragment frag = new PassphraseDialogFragment(); PassphraseDialogFragment frag = new PassphraseDialogFragment();
Bundle args = new Bundle(); Bundle args = new Bundle();
args.putLong(EXTRA_SECRET_KEY_ID, keyId); args.putLong(EXTRA_SUBKEY_ID, keyId);
frag.setArguments(args); frag.setArguments(args);
@ -116,7 +116,7 @@ public class PassphraseDialogActivity extends FragmentActivity {
@Override @Override
public Dialog onCreateDialog(Bundle savedInstanceState) { public Dialog onCreateDialog(Bundle savedInstanceState) {
final Activity activity = getActivity(); final Activity activity = getActivity();
mSubKeyId = getArguments().getLong(EXTRA_SECRET_KEY_ID); mSubKeyId = getArguments().getLong(EXTRA_SUBKEY_ID);
CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(activity); CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(activity);

View File

@ -2,12 +2,14 @@ package org.sufficientlysecure.keychain.ui.dialog;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.view.ContextThemeWrapper;
import android.view.View; import android.view.View;
import android.widget.TextView; import android.widget.TextView;
import org.sufficientlysecure.keychain.R; 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. * 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 * Also, the dialog NEEDS to be called with show() directly, not create(), otherwise
* the order of internal operations will lead to a crash! * 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 class CustomAlertDialogBuilder extends AlertDialog.Builder {
public CustomAlertDialogBuilder(Activity activity) { 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 @Override

View File

@ -13,7 +13,7 @@
android:orientation="vertical"> android:orientation="vertical">
<EditText <EditText
android:id="@+id/message" android:id="@+id/encrypt_text_text"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dip" android:layout_height="0dip"
android:gravity="top" 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_key_warn">"Bad key for encryption: %s"</string>
<string name="msg_se_ok">"Sign/Encrypt operation successful!"</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_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_signing">"Signing data (without encryption)"</string>
<string name="msg_se_sigcrypting">"Encrypting data with signature"</string> <string name="msg_se_sigcrypting">"Encrypting data with signature"</string>
<string name="msg_se">"Starting sign and/or encrypt operation"</string> <string name="msg_se">"Starting sign and/or encrypt operation"</string>