mirror of
https://github.com/moparisthebest/open-keychain
synced 2024-11-23 17:22:16 -05:00
Work on new result handling (WIP)
This commit is contained in:
parent
3defd194aa
commit
d686c55a0a
@ -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;
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -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]);
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -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;
|
||||
|
@ -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),
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user