aboutsummaryrefslogtreecommitdiffstats
path: root/OpenKeychain/src/main/java/org
diff options
context:
space:
mode:
authorDominik Schürmann <dominik@dominikschuermann.de>2014-08-14 13:31:01 +0200
committerDominik Schürmann <dominik@dominikschuermann.de>2014-08-14 13:31:01 +0200
commit6da17ef6bbe9569e53268b446e59dcc69aaa2da4 (patch)
tree0747b0f06b974f032573f48c08930498050ab9ce /OpenKeychain/src/main/java/org
parent881a50207af0a9f9f5aa69f451110de786779b54 (diff)
parent37edd0f390064e725d1b5c1c866a76c9922b0f64 (diff)
downloadopen-keychain-6da17ef6bbe9569e53268b446e59dcc69aaa2da4.tar.gz
open-keychain-6da17ef6bbe9569e53268b446e59dcc69aaa2da4.tar.bz2
open-keychain-6da17ef6bbe9569e53268b446e59dcc69aaa2da4.zip
Merge branch 'master' into yubikey
Conflicts: OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java
Diffstat (limited to 'OpenKeychain/src/main/java/org')
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java8
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpSignatureResultBuilder.java4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java169
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyResult.java23
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java8
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java10
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java109
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java3
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java3
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java7
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java84
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java80
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java97
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java102
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java9
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java95
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java14
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java67
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java44
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFileFragment.java10
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java18
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java28
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java25
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/UserIdInfoDialogFragment.java95
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java35
28 files changed, 822 insertions, 333 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java
index 7d1af704d..755d74ac2 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java
@@ -66,7 +66,7 @@ public final class Constants {
public static final String LANGUAGE = "language";
public static final String KEY_SERVERS = "keyServers";
public static final String KEY_SERVERS_DEFAULT_VERSION = "keyServersDefaultVersion";
- public static final String CONCEAL_PGP_APPLICATION = "concealPgpApplication";
+ public static final String WRITE_VERSION_HEADER = "writeVersionHeader";
public static final String FIRST_TIME = "firstTime";
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java
index 491709354..72e88d793 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java
@@ -195,13 +195,13 @@ public class Preferences {
}
}
- public void setConcealPgpApplication(boolean conceal) {
+ public void setWriteVersionHeader(boolean conceal) {
SharedPreferences.Editor editor = mSharedPreferences.edit();
- editor.putBoolean(Constants.Pref.CONCEAL_PGP_APPLICATION, conceal);
+ editor.putBoolean(Constants.Pref.WRITE_VERSION_HEADER, conceal);
editor.commit();
}
- public boolean getConcealPgpApplication() {
- return mSharedPreferences.getBoolean(Constants.Pref.CONCEAL_PGP_APPLICATION, false);
+ public boolean getWriteVersionHeader() {
+ return mSharedPreferences.getBoolean(Constants.Pref.WRITE_VERSION_HEADER, false);
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpSignatureResultBuilder.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpSignatureResultBuilder.java
index 75f8bdb66..a116ea665 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpSignatureResultBuilder.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpSignatureResultBuilder.java
@@ -65,6 +65,10 @@ public class OpenPgpSignatureResultBuilder {
this.mSignatureAvailable = signatureAvailable;
}
+ public boolean isValidSignature() {
+ return mValidSignature;
+ }
+
public OpenPgpSignatureResult build() {
if (mSignatureAvailable) {
OpenPgpSignatureResult result = new OpenPgpSignatureResult();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
index 7f2d971ed..7d113241b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
@@ -18,6 +18,9 @@
package org.sufficientlysecure.keychain.pgp;
+import android.webkit.MimeTypeMap;
+
+import org.openintents.openpgp.OpenPgpMetadata;
import org.spongycastle.bcpg.ArmoredInputStream;
import org.spongycastle.openpgp.PGPCompressedData;
import org.spongycastle.openpgp.PGPEncryptedData;
@@ -43,8 +46,10 @@ import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.ProgressScaler;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
@@ -52,6 +57,7 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.net.URLConnection;
import java.security.SignatureException;
import java.util.Iterator;
import java.util.Set;
@@ -69,6 +75,7 @@ public class PgpDecryptVerify {
private boolean mAllowSymmetricDecryption;
private String mPassphrase;
private Set<Long> mAllowedKeyIds;
+ private boolean mDecryptMetadataOnly;
private PgpDecryptVerify(Builder builder) {
// private Constructor can only be called from Builder
@@ -81,6 +88,7 @@ public class PgpDecryptVerify {
this.mAllowSymmetricDecryption = builder.mAllowSymmetricDecryption;
this.mPassphrase = builder.mPassphrase;
this.mAllowedKeyIds = builder.mAllowedKeyIds;
+ this.mDecryptMetadataOnly = builder.mDecryptMetadataOnly;
}
public static class Builder {
@@ -95,6 +103,7 @@ public class PgpDecryptVerify {
private boolean mAllowSymmetricDecryption = true;
private String mPassphrase = null;
private Set<Long> mAllowedKeyIds = null;
+ private boolean mDecryptMetadataOnly = false;
public Builder(ProviderHelper providerHelper, PassphraseCache passphraseCache,
InputData data, OutputStream outStream) {
@@ -124,7 +133,16 @@ public class PgpDecryptVerify {
* This means only ciphertexts encrypted for one of these private key can be decrypted.
*/
public Builder setAllowedKeyIds(Set<Long> allowedKeyIds) {
- this.mAllowedKeyIds = allowedKeyIds;
+ mAllowedKeyIds = allowedKeyIds;
+ return this;
+ }
+
+ /**
+ * If enabled, the actual decryption/verification of the content will not be executed.
+ * The metadata only will be decrypted and returned.
+ */
+ public Builder setDecryptMetadataOnly(boolean decryptMetadataOnly) {
+ mDecryptMetadataOnly = decryptMetadataOnly;
return this;
}
@@ -146,7 +164,8 @@ public class PgpDecryptVerify {
}
public interface PassphraseCache {
- public String getCachedPassphrase(long masterKeyId);
+ public String getCachedPassphrase(long masterKeyId)
+ throws NoSecretKeyException;
}
public static class InvalidDataException extends Exception {
@@ -227,8 +246,6 @@ public class PgpDecryptVerify {
InputStream clear;
PGPEncryptedData encryptedData;
- currentProgress += 5;
-
PGPPublicKeyEncryptedData encryptedDataAsymmetric = null;
PGPPBEEncryptedData encryptedDataSymmetric = null;
CanonicalizedSecretKey secretEncryptionKey = null;
@@ -239,6 +256,7 @@ public class PgpDecryptVerify {
while (it.hasNext()) {
Object obj = it.next();
if (obj instanceof PGPPublicKeyEncryptedData) {
+ currentProgress += 2;
updateProgress(R.string.progress_finding_key, currentProgress, 100);
PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) obj;
@@ -269,8 +287,8 @@ public class PgpDecryptVerify {
// allow only specific keys for decryption?
if (mAllowedKeyIds != null) {
- Log.d(Constants.TAG, "encData.getKeyID():" + encData.getKeyID());
- Log.d(Constants.TAG, "allowedKeyIds: " + mAllowedKeyIds);
+ Log.d(Constants.TAG, "encData.getKeyID(): " + encData.getKeyID());
+ Log.d(Constants.TAG, "mAllowedKeyIds: " + mAllowedKeyIds);
Log.d(Constants.TAG, "masterKeyId: " + masterKeyId);
if (!mAllowedKeyIds.contains(masterKeyId)) {
@@ -325,6 +343,7 @@ public class PgpDecryptVerify {
}
if (symmetricPacketFound) {
+ currentProgress += 2;
updateProgress(R.string.progress_preparing_streams, currentProgress, 100);
PGPDigestCalculatorProvider digestCalcProvider = new JcaPGPDigestCalculatorProviderBuilder()
@@ -336,26 +355,23 @@ public class PgpDecryptVerify {
clear = encryptedDataSymmetric.getDataStream(decryptorFactory);
encryptedData = encryptedDataSymmetric;
- currentProgress += 5;
} else if (asymmetricPacketFound) {
- currentProgress += 5;
+ currentProgress += 2;
updateProgress(R.string.progress_extracting_key, currentProgress, 100);
try {
if (!secretEncryptionKey.unlock(mPassphrase)) {
throw new WrongPassphraseException();
}
- } catch(PgpGeneralException e) {
+ } catch (PgpGeneralException e) {
throw new KeyExtractionException();
}
- currentProgress += 5;
+
+ currentProgress += 2;
updateProgress(R.string.progress_preparing_streams, currentProgress, 100);
PublicKeyDataDecryptorFactory decryptorFactory = secretEncryptionKey.getDecryptorFactory();
-
clear = encryptedDataAsymmetric.getDataStream(decryptorFactory);
-
encryptedData = encryptedDataAsymmetric;
- currentProgress += 5;
} else {
// no packet has been found where we have the corresponding secret key in our db
throw new NoSecretKeyException();
@@ -369,18 +385,19 @@ public class PgpDecryptVerify {
CanonicalizedPublicKey signingKey = null;
if (dataChunk instanceof PGPCompressedData) {
+ currentProgress += 2;
updateProgress(R.string.progress_decompressing_data, currentProgress, 100);
- PGPObjectFactory fact = new PGPObjectFactory(
- ((PGPCompressedData) dataChunk).getDataStream());
+ PGPCompressedData compressedData = (PGPCompressedData) dataChunk;
+
+ PGPObjectFactory fact = new PGPObjectFactory(compressedData.getDataStream());
dataChunk = fact.nextObject();
plainFact = fact;
- currentProgress += 10;
}
PGPOnePassSignature signature = null;
-
if (dataChunk instanceof PGPOnePassSignatureList) {
+ currentProgress += 2;
updateProgress(R.string.progress_processing_signature, currentProgress, 100);
PGPOnePassSignatureList sigList = (PGPOnePassSignatureList) dataChunk;
@@ -396,8 +413,7 @@ public class PgpDecryptVerify {
signingKey = signingRing.getPublicKey(sigKeyId);
signatureIndex = i;
} catch (ProviderHelper.NotFoundException e) {
- Log.d(Constants.TAG, "key not found!");
- // try next one...
+ Log.d(Constants.TAG, "key not found, trying next signature...");
}
}
@@ -410,8 +426,8 @@ public class PgpDecryptVerify {
signatureResultBuilder.keyId(signingRing.getMasterKeyId());
try {
signatureResultBuilder.userId(signingRing.getPrimaryUserIdWithFallback());
- } catch(PgpGeneralException e) {
- Log.d(Constants.TAG, "No primary user id in key " + signingRing.getMasterKeyId());
+ } catch (PgpGeneralException e) {
+ Log.d(Constants.TAG, "No primary user id in keyring with master key id " + signingRing.getMasterKeyId());
}
signatureResultBuilder.signatureKeyCertified(signingRing.getVerified() > 0);
@@ -429,56 +445,104 @@ public class PgpDecryptVerify {
}
dataChunk = plainFact.nextObject();
- currentProgress += 10;
}
if (dataChunk instanceof PGPSignatureList) {
+ // skip
dataChunk = plainFact.nextObject();
}
if (dataChunk instanceof PGPLiteralData) {
+ currentProgress += 4;
updateProgress(R.string.progress_decrypting, currentProgress, 100);
PGPLiteralData literalData = (PGPLiteralData) dataChunk;
- byte[] buffer = new byte[1 << 16];
- InputStream dataIn = literalData.getInputStream();
+ // TODO: how to get the real original size?
+ // this is the encrypted size so if we enable compression this value is wrong!
+ long originalSize = mData.getSize() - mData.getStreamPosition();
+ if (originalSize < 0) {
+ originalSize = 0;
+ }
+
+ String originalFilename = literalData.getFileName();
+ String mimeType = null;
+ if (literalData.getFormat() == PGPLiteralData.TEXT
+ || literalData.getFormat() == PGPLiteralData.UTF8) {
+ mimeType = "text/plain";
+ } else {
+ // TODO: better would be: https://github.com/open-keychain/open-keychain/issues/753
+
+ // try to guess from file ending
+ String extension = MimeTypeMap.getFileExtensionFromUrl(originalFilename);
+ if (extension != null) {
+ MimeTypeMap mime = MimeTypeMap.getSingleton();
+ mimeType = mime.getMimeTypeFromExtension(extension);
+ }
+ if (mimeType == null) {
+ mimeType = URLConnection.guessContentTypeFromName(originalFilename);
+ }
+ if (mimeType == null) {
+ mimeType = "*/*";
+ }
+ }
+
+ OpenPgpMetadata metadata = new OpenPgpMetadata(
+ originalFilename,
+ mimeType,
+ literalData.getModificationTime().getTime(),
+ originalSize);
+ result.setDecryptMetadata(metadata);
- int startProgress = currentProgress;
- int endProgress = 100;
+ Log.d(Constants.TAG, "metadata: " + metadata);
+
+ // return here if we want to decrypt the metadata only
+ if (mDecryptMetadataOnly) {
+ return result;
+ }
+
+ int endProgress;
if (signature != null) {
endProgress = 90;
} else if (encryptedData.isIntegrityProtected()) {
endProgress = 95;
+ } else {
+ endProgress = 100;
}
+ ProgressScaler progressScaler =
+ new ProgressScaler(mProgressable, currentProgress, endProgress, 100);
- int n;
- // TODO: progress calculation is broken here! Try to rework it based on commented code!
-// int progress = 0;
- long startPos = mData.getStreamPosition();
- while ((n = dataIn.read(buffer)) > 0) {
- mOutStream.write(buffer, 0, n);
-// progress += n;
+ InputStream dataIn = literalData.getInputStream();
+
+ long alreadyWritten = 0;
+ long wholeSize = mData.getSize() - mData.getStreamPosition();
+ int length;
+ byte[] buffer = new byte[1 << 16];
+ while ((length = dataIn.read(buffer)) > 0) {
+ mOutStream.write(buffer, 0, length);
+
+ // update signature buffer if signature is also present
if (signature != null) {
try {
- signature.update(buffer, 0, n);
+ signature.update(buffer, 0, length);
} catch (SignatureException e) {
- Log.d(Constants.TAG, "SIGNATURE_ERROR");
+ Log.e(Constants.TAG, "SignatureException -> Not a valid signature!", e);
signatureResultBuilder.validSignature(false);
signature = null;
}
}
- // TODO: dead code?!
- // unknown size, but try to at least have a moving, slowing down progress bar
-// currentProgress = startProgress + (endProgress - startProgress) * progress
-// / (progress + 100000);
- if (mData.getSize() - startPos == 0) {
- currentProgress = endProgress;
+
+ alreadyWritten += length;
+ if (wholeSize > 0) {
+ long progress = 100 * alreadyWritten / wholeSize;
+ // stop at 100% for wrong file sizes...
+ if (progress > 100) {
+ progress = 100;
+ }
+ progressScaler.setProgress((int) progress, 100);
} else {
- currentProgress = (int) (startProgress + (endProgress - startProgress)
- * (mData.getStreamPosition() - startPos) / (mData.getSize() - startPos));
+ // TODO: slow annealing to fake a progress?
}
- updateProgress(currentProgress, 100);
}
if (signature != null) {
@@ -510,8 +574,14 @@ public class PgpDecryptVerify {
}
} else {
// no integrity check
- Log.e(Constants.TAG, "Encrypted data was not integrity protected!");
- // TODO: inform user?
+ Log.d(Constants.TAG, "Encrypted data was not integrity protected! MDC packet is missing!");
+
+ // If no valid signature is present:
+ // Handle missing integrity protection like failed integrity protection!
+ // The MDC packet can be stripped by an attacker!
+ if (!signatureResultBuilder.isValidSignature()) {
+ throw new IntegrityCheckFailedException();
+ }
}
updateProgress(R.string.progress_done, 100, 100);
@@ -581,8 +651,7 @@ public class PgpDecryptVerify {
signingKey = signingRing.getPublicKey(sigKeyId);
signatureIndex = i;
} catch (ProviderHelper.NotFoundException e) {
- Log.d(Constants.TAG, "key not found!");
- // try next one...
+ Log.d(Constants.TAG, "key not found, trying next signature...");
}
}
@@ -597,8 +666,8 @@ public class PgpDecryptVerify {
signatureResultBuilder.keyId(signingRing.getMasterKeyId());
try {
signatureResultBuilder.userId(signingRing.getPrimaryUserIdWithFallback());
- } catch(PgpGeneralException e) {
- Log.d(Constants.TAG, "No primary user id in key " + signingRing.getMasterKeyId());
+ } catch (PgpGeneralException e) {
+ Log.d(Constants.TAG, "No primary user id in key with master key id " + signingRing.getMasterKeyId());
}
signatureResultBuilder.signatureKeyCertified(signingRing.getVerified() > 0);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyResult.java
index ad240e834..506f48c52 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyResult.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyResult.java
@@ -20,6 +20,7 @@ package org.sufficientlysecure.keychain.pgp;
import android.os.Parcel;
import android.os.Parcelable;
+import org.openintents.openpgp.OpenPgpMetadata;
import org.openintents.openpgp.OpenPgpSignatureResult;
public class PgpDecryptVerifyResult implements Parcelable {
@@ -31,21 +32,22 @@ public class PgpDecryptVerifyResult implements Parcelable {
long mKeyIdPassphraseNeeded;
OpenPgpSignatureResult mSignatureResult;
+ OpenPgpMetadata mDecryptMetadata;
public int getStatus() {
return mStatus;
}
- public void setStatus(int mStatus) {
- this.mStatus = mStatus;
+ public void setStatus(int status) {
+ mStatus = status;
}
public long getKeyIdPassphraseNeeded() {
return mKeyIdPassphraseNeeded;
}
- public void setKeyIdPassphraseNeeded(long mKeyIdPassphraseNeeded) {
- this.mKeyIdPassphraseNeeded = mKeyIdPassphraseNeeded;
+ public void setKeyIdPassphraseNeeded(long keyIdPassphraseNeeded) {
+ mKeyIdPassphraseNeeded = keyIdPassphraseNeeded;
}
public OpenPgpSignatureResult getSignatureResult() {
@@ -53,7 +55,15 @@ public class PgpDecryptVerifyResult implements Parcelable {
}
public void setSignatureResult(OpenPgpSignatureResult signatureResult) {
- this.mSignatureResult = signatureResult;
+ mSignatureResult = signatureResult;
+ }
+
+ public OpenPgpMetadata getDecryptMetadata() {
+ return mDecryptMetadata;
+ }
+
+ public void setDecryptMetadata(OpenPgpMetadata decryptMetadata) {
+ mDecryptMetadata = decryptMetadata;
}
public PgpDecryptVerifyResult() {
@@ -64,6 +74,7 @@ public class PgpDecryptVerifyResult implements Parcelable {
this.mStatus = b.mStatus;
this.mKeyIdPassphraseNeeded = b.mKeyIdPassphraseNeeded;
this.mSignatureResult = b.mSignatureResult;
+ this.mDecryptMetadata = b.mDecryptMetadata;
}
@@ -75,6 +86,7 @@ public class PgpDecryptVerifyResult implements Parcelable {
dest.writeInt(mStatus);
dest.writeLong(mKeyIdPassphraseNeeded);
dest.writeParcelable(mSignatureResult, 0);
+ dest.writeParcelable(mDecryptMetadata, 0);
}
public static final Creator<PgpDecryptVerifyResult> CREATOR = new Creator<PgpDecryptVerifyResult>() {
@@ -83,6 +95,7 @@ public class PgpDecryptVerifyResult implements Parcelable {
vr.mStatus = source.readInt();
vr.mKeyIdPassphraseNeeded = source.readLong();
vr.mSignatureResult = source.readParcelable(OpenPgpSignatureResult.class.getClassLoader());
+ vr.mDecryptMetadata = source.readParcelable(OpenPgpMetadata.class.getClassLoader());
return vr;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java
index 0ceefc4d9..1cf027721 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java
@@ -60,11 +60,11 @@ public class PgpHelper {
}
}
- public static String getFullVersion(Context context) {
- if(Preferences.getPreferences(context).getConcealPgpApplication()){
- return "";
- } else {
+ public static String getVersionForHeader(Context context) {
+ if(Preferences.getPreferences(context).getWriteVersionHeader()){
return "OpenKeychain v" + getVersion(context);
+ } else {
+ return null;
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java
index 846b00ef2..f14eacda2 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java
@@ -230,7 +230,10 @@ public class PgpImportExport {
progress++;
// Create an output stream
ArmoredOutputStream arOutStream = new ArmoredOutputStream(outStream);
- arOutStream.setHeader("Version", PgpHelper.getFullVersion(mContext));
+ String version = PgpHelper.getVersionForHeader(mContext);
+ if (version != null) {
+ arOutStream.setHeader("Version", version);
+ }
updateProgress(progress * 100 / masterKeyIdsSize, 100);
@@ -258,7 +261,10 @@ public class PgpImportExport {
progress++;
// Create an output stream
ArmoredOutputStream arOutStream = new ArmoredOutputStream(outStream);
- arOutStream.setHeader("Version", PgpHelper.getFullVersion(mContext));
+ String version = PgpHelper.getVersionForHeader(mContext);
+ if (version != null) {
+ arOutStream.setHeader("Version", version);
+ }
updateProgress(progress * 100 / masterKeyIdsSize, 100);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java
index 67eced699..901611982 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java
@@ -37,6 +37,7 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.ProgressScaler;
import java.io.BufferedReader;
import java.io.IOException;
@@ -71,6 +72,7 @@ public class PgpSignEncrypt {
private String mSignaturePassphrase;
private boolean mEncryptToSigner;
private boolean mCleartextInput;
+ private String mOriginalFilename;
private byte[] mNfcSignedHash = null;
private Date mNfcCreationTimestamp = null;
@@ -105,16 +107,17 @@ public class PgpSignEncrypt {
this.mCleartextInput = builder.mCleartextInput;
this.mNfcSignedHash = builder.mNfcSignedHash;
this.mNfcCreationTimestamp = builder.mNfcCreationTimestamp;
+ this.mOriginalFilename = builder.mOriginalFilename;
}
public static class Builder {
// mandatory parameter
private ProviderHelper mProviderHelper;
- private String mVersionHeader;
private InputData mData;
private OutputStream mOutStream;
// optional
+ private String mVersionHeader = null;
private Progressable mProgressable = null;
private boolean mEnableAsciiArmorOutput = false;
private int mCompressionId = CompressionAlgorithmTags.UNCOMPRESSED;
@@ -126,15 +129,19 @@ public class PgpSignEncrypt {
private String mSignaturePassphrase = null;
private boolean mEncryptToSigner = false;
private boolean mCleartextInput = false;
-
+ private String mOriginalFilename = "";
private byte[] mNfcSignedHash = null;
private Date mNfcCreationTimestamp = null;
- public Builder(ProviderHelper providerHelper, String versionHeader, InputData data, OutputStream outStream) {
- this.mProviderHelper = providerHelper;
- this.mVersionHeader = versionHeader;
- this.mData = data;
- this.mOutStream = outStream;
+ public Builder(ProviderHelper providerHelper, InputData data, OutputStream outStream) {
+ mProviderHelper = providerHelper;
+ mData = data;
+ mOutStream = outStream;
+ }
+
+ public Builder setVersionHeader(String versionHeader) {
+ mVersionHeader = versionHeader;
+ return this;
}
public Builder setProgressable(Progressable progressable) {
@@ -153,12 +160,12 @@ public class PgpSignEncrypt {
}
public Builder setEncryptionMasterKeyIds(long[] encryptionMasterKeyIds) {
- this.mEncryptionMasterKeyIds = encryptionMasterKeyIds;
+ mEncryptionMasterKeyIds = encryptionMasterKeyIds;
return this;
}
public Builder setSymmetricPassphrase(String symmetricPassphrase) {
- this.mSymmetricPassphrase = symmetricPassphrase;
+ mSymmetricPassphrase = symmetricPassphrase;
return this;
}
@@ -183,11 +190,13 @@ public class PgpSignEncrypt {
}
/**
+ * Also encrypt with the signing keyring
+ *
* @param encryptToSigner
* @return
*/
public Builder setEncryptToSigner(boolean encryptToSigner) {
- this.mEncryptToSigner = encryptToSigner;
+ mEncryptToSigner = encryptToSigner;
return this;
}
@@ -198,7 +207,12 @@ public class PgpSignEncrypt {
* @return
*/
public Builder setCleartextInput(boolean cleartextInput) {
- this.mCleartextInput = cleartextInput;
+ mCleartextInput = cleartextInput;
+ return this;
+ }
+
+ public Builder setOriginalFilename(String originalFilename) {
+ mOriginalFilename = originalFilename;
return this;
}
@@ -278,6 +292,18 @@ public class PgpSignEncrypt {
mEncryptionMasterKeyIds[mEncryptionMasterKeyIds.length - 1] = mSignatureMasterKeyId;
}
+ ArmoredOutputStream armorOut = null;
+ OutputStream out;
+ if (mEnableAsciiArmorOutput) {
+ armorOut = new ArmoredOutputStream(mOutStream);
+ if (mVersionHeader != null) {
+ armorOut.setHeader("Version", mVersionHeader);
+ }
+ out = armorOut;
+ } else {
+ out = mOutStream;
+ }
+
/* Get keys for signature generation for later usage */
CanonicalizedSecretKey signingKey = null;
if (enableSignature) {
@@ -314,7 +340,7 @@ public class PgpSignEncrypt {
mSignatureHashAlgorithm = supported.getLast();
}
}
- updateProgress(R.string.progress_preparing_streams, 5, 100);
+ updateProgress(R.string.progress_preparing_streams, 2, 100);
/* Initialize PGPEncryptedDataGenerator for later usage */
PGPEncryptedDataGenerator cPk = null;
@@ -354,7 +380,7 @@ public class PgpSignEncrypt {
/* Initialize signature generator object for later usage */
PGPSignatureGenerator signatureGenerator = null;
if (enableSignature) {
- updateProgress(R.string.progress_preparing_signature, 10, 100);
+ updateProgress(R.string.progress_preparing_signature, 4, 100);
try {
boolean cleartext = mCleartextInput && mEnableAsciiArmorOutput && !enableEncryption;
@@ -366,16 +392,8 @@ public class PgpSignEncrypt {
}
}
- ArmoredOutputStream armorOut = null;
- OutputStream out;
- if (mEnableAsciiArmorOutput) {
- armorOut = new ArmoredOutputStream(mOutStream);
- armorOut.setHeader("Version", mVersionHeader);
- out = armorOut;
- } else {
- out = mOutStream;
- }
-
+ ProgressScaler progressScaler =
+ new ProgressScaler(mProgressable, 8, 95, 100);
PGPCompressedDataGenerator compressGen = null;
OutputStream pOut = null;
OutputStream encryptionOut = null;
@@ -383,6 +401,7 @@ public class PgpSignEncrypt {
if (enableEncryption) {
/* actual encryption */
+ updateProgress(R.string.progress_encrypting, 8, 100);
encryptionOut = cPk.open(out, new byte[1 << 16]);
@@ -398,26 +417,25 @@ public class PgpSignEncrypt {
}
PGPLiteralDataGenerator literalGen = new PGPLiteralDataGenerator();
- // file name not needed, so empty string
- pOut = literalGen.open(bcpgOut, PGPLiteralData.BINARY, "", new Date(),
+ pOut = literalGen.open(bcpgOut, PGPLiteralData.BINARY, mOriginalFilename, new Date(),
new byte[1 << 16]);
- updateProgress(R.string.progress_encrypting, 20, 100);
- long progress = 0;
- int n;
+ long alreadyWritten = 0;
+ int length;
byte[] buffer = new byte[1 << 16];
InputStream in = mData.getInputStream();
- while ((n = in.read(buffer)) > 0) {
- pOut.write(buffer, 0, n);
+ while ((length = in.read(buffer)) > 0) {
+ pOut.write(buffer, 0, length);
// update signature buffer if signature is requested
if (enableSignature) {
- signatureGenerator.update(buffer, 0, n);
+ signatureGenerator.update(buffer, 0, length);
}
- progress += n;
- if (mData.getSize() != 0) {
- updateProgress((int) (20 + (95 - 20) * progress / mData.getSize()), 100);
+ alreadyWritten += length;
+ if (mData.getSize() > 0) {
+ long progress = 100 * alreadyWritten / mData.getSize();
+ progressScaler.setProgress((int) progress, 100);
}
}
@@ -425,7 +443,7 @@ public class PgpSignEncrypt {
} else if (enableSignature && mCleartextInput && mEnableAsciiArmorOutput) {
/* cleartext signature: sign-only of ascii text */
- updateProgress(R.string.progress_signing, 40, 100);
+ updateProgress(R.string.progress_signing, 8, 100);
// write -----BEGIN PGP SIGNED MESSAGE-----
armorOut.beginClearText(mSignatureHashAlgorithm);
@@ -436,6 +454,7 @@ public class PgpSignEncrypt {
// update signature buffer with first line
processLine(reader.readLine(), armorOut, signatureGenerator);
+ // TODO: progress: fake annealing?
while (true) {
String line = reader.readLine();
@@ -458,7 +477,7 @@ public class PgpSignEncrypt {
} else if (enableSignature && !mCleartextInput) {
/* sign-only binary (files/data stream) */
- updateProgress(R.string.progress_signing, 40, 100);
+ updateProgress(R.string.progress_signing, 8, 100);
InputStream in = mData.getInputStream();
@@ -472,16 +491,22 @@ public class PgpSignEncrypt {
signatureGenerator.generateOnePassVersion(false).encode(bcpgOut);
PGPLiteralDataGenerator literalGen = new PGPLiteralDataGenerator();
- // file name not needed, so empty string
- pOut = literalGen.open(bcpgOut, PGPLiteralData.BINARY, "", new Date(),
+ pOut = literalGen.open(bcpgOut, PGPLiteralData.BINARY, mOriginalFilename, new Date(),
new byte[1 << 16]);
+ long alreadyWritten = 0;
+ int length;
byte[] buffer = new byte[1 << 16];
- int n;
- while ((n = in.read(buffer)) > 0) {
- pOut.write(buffer, 0, n);
+ while ((length = in.read(buffer)) > 0) {
+ pOut.write(buffer, 0, length);
+
+ signatureGenerator.update(buffer, 0, length);
- signatureGenerator.update(buffer, 0, n);
+ alreadyWritten += length;
+ if (mData.getSize() > 0) {
+ long progress = 100 * alreadyWritten / mData.getSize();
+ progressScaler.setProgress((int) progress, 100);
+ }
}
literalGen.close();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
index 18ddaa0ec..73a51942d 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
@@ -204,7 +204,9 @@ public class UncachedKeyRing {
public void encodeArmored(OutputStream out, String version) throws IOException {
ArmoredOutputStream aos = new ArmoredOutputStream(out);
- aos.setHeader("Version", version);
+ if (version != null) {
+ aos.setHeader("Version", version);
+ }
aos.write(mRing.getEncoded());
aos.close();
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java
index 56168847f..dd59f8603 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java
@@ -105,8 +105,9 @@ public class KeychainContract {
public static class KeyRings implements BaseColumns, KeysColumns, UserIdsColumns {
public static final String MASTER_KEY_ID = KeysColumns.MASTER_KEY_ID;
- public static final String IS_REVOKED = KeysColumns.IS_REVOKED;
+ public static final String IS_REVOKED = KeychainDatabase.Tables.KEYS + "." + KeysColumns.IS_REVOKED;
public static final String VERIFIED = CertsColumns.VERIFIED;
+ public static final String IS_EXPIRED = "is_expired";
public static final String HAS_ANY_SECRET = "has_any_secret";
public static final String HAS_ENCRYPT = "has_encrypt";
public static final String HAS_SIGN = "has_sign";
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
index b651069e9..c914cb5b7 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
@@ -271,6 +271,9 @@ public class KeychainProvider extends ContentProvider {
"kE." + Keys.KEY_ID + " AS " + KeyRings.HAS_ENCRYPT);
projectionMap.put(KeyRings.HAS_SIGN,
"kS." + Keys.KEY_ID + " AS " + KeyRings.HAS_SIGN);
+ projectionMap.put(KeyRings.IS_EXPIRED,
+ "(" + Tables.KEYS + "." + Keys.EXPIRY + " IS NOT NULL AND " + Tables.KEYS + "." + Keys.EXPIRY
+ + " < " + new Date().getTime() / 1000 + ") AS " + KeyRings.IS_EXPIRED);
qb.setProjectionMap(projectionMap);
// Need this as list so we can search in it
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
index 6111a4ef4..a13bb9c98 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
@@ -862,7 +862,10 @@ public class ProviderHelper {
UncachedKeyRing keyRing = UncachedKeyRing.decodeFromData(data);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
- keyRing.encodeArmored(bos, PgpHelper.getFullVersion(mContext));
+ String version = PgpHelper.getVersionForHeader(mContext);
+ if (version != null) {
+ keyRing.encodeArmored(bos, version);
+ }
String armoredKey = bos.toString("UTF-8");
Log.d(Constants.TAG, "armoredKey:" + armoredKey);
@@ -925,7 +928,7 @@ public class ProviderHelper {
mContentResolver.insert(uri, contentValueForApiAccounts(accSettings));
}
- public void updateApiAccount(AccountSettings accSettings, Uri uri) {
+ public void updateApiAccount(Uri uri, AccountSettings accSettings) {
if (mContentResolver.update(uri, contentValueForApiAccounts(accSettings), null,
null) <= 0) {
throw new RuntimeException();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
index eae217e16..6bc623b85 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
@@ -25,6 +25,7 @@ import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import org.openintents.openpgp.IOpenPgpService;
+import org.openintents.openpgp.OpenPgpMetadata;
import org.openintents.openpgp.OpenPgpError;
import org.openintents.openpgp.OpenPgpSignatureResult;
import org.openintents.openpgp.util.OpenPgpApi;
@@ -184,7 +185,13 @@ public class OpenPgpService extends RemoteService {
if (data.hasExtra(OpenPgpApi.EXTRA_PASSPHRASE)) {
passphrase = data.getStringExtra(OpenPgpApi.EXTRA_PASSPHRASE);
} else {
- passphrase = PassphraseCacheService.getCachedPassphrase(getContext(), accSettings.getKeyId());
+ try {
+ passphrase = PassphraseCacheService.getCachedPassphrase(getContext(), accSettings.getKeyId());
+ } catch (PassphraseCacheService.KeyNotFoundException e) {
+ // secret key that is set for this account is deleted?
+ // show account config again!
+ return getCreateAccountIntent(data, data.getStringExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME));
+ }
}
if (passphrase == null) {
// get PendingIntent for passphrase input, add it to given params and return to client
@@ -204,9 +211,9 @@ public class OpenPgpService extends RemoteService {
// sign-only
PgpSignEncrypt.Builder builder = new PgpSignEncrypt.Builder(
new ProviderHelper(getContext()),
- PgpHelper.getFullVersion(getContext()),
inputData, os);
builder.setEnableAsciiArmorOutput(asciiArmor)
+ .setVersionHeader(PgpHelper.getVersionForHeader(this))
.setSignatureHashAlgorithm(accSettings.getHashAlgorithm())
.setSignatureMasterKeyId(accSettings.getKeyId())
.setSignaturePassphrase(passphrase)
@@ -257,6 +264,10 @@ public class OpenPgpService extends RemoteService {
boolean sign) {
try {
boolean asciiArmor = data.getBooleanExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
+ String originalFilename = data.getStringExtra(OpenPgpApi.EXTRA_ORIGINAL_FILENAME);
+ if (originalFilename == null) {
+ originalFilename = "";
+ }
long[] keyIds;
if (data.hasExtra(OpenPgpApi.EXTRA_KEY_IDS)) {
@@ -297,12 +308,13 @@ public class OpenPgpService extends RemoteService {
PgpSignEncrypt.Builder builder = new PgpSignEncrypt.Builder(
new ProviderHelper(getContext()),
- PgpHelper.getFullVersion(getContext()),
inputData, os);
builder.setEnableAsciiArmorOutput(asciiArmor)
+ .setVersionHeader(PgpHelper.getVersionForHeader(this))
.setCompressionId(accSettings.getCompression())
.setSymmetricEncryptionAlgorithm(accSettings.getEncryptionAlgorithm())
- .setEncryptionMasterKeyIds(keyIds);
+ .setEncryptionMasterKeyIds(keyIds)
+ .setOriginalFilename(originalFilename);
if (sign) {
String passphrase;
@@ -359,11 +371,18 @@ public class OpenPgpService extends RemoteService {
}
private Intent decryptAndVerifyImpl(Intent data, ParcelFileDescriptor input,
- ParcelFileDescriptor output, Set<Long> allowedKeyIds) {
+ ParcelFileDescriptor output, Set<Long> allowedKeyIds,
+ boolean decryptMetadataOnly) {
try {
// Get Input- and OutputStream from ParcelFileDescriptor
InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(input);
- OutputStream os = new ParcelFileDescriptor.AutoCloseOutputStream(output);
+
+ OutputStream os;
+ if (decryptMetadataOnly) {
+ os = null;
+ } else {
+ os = new ParcelFileDescriptor.AutoCloseOutputStream(output);
+ }
Intent result = new Intent();
try {
@@ -376,17 +395,24 @@ public class OpenPgpService extends RemoteService {
new ProviderHelper(this),
new PgpDecryptVerify.PassphraseCache() {
@Override
- public String getCachedPassphrase(long masterKeyId) {
- return PassphraseCacheService.getCachedPassphrase(
- OpenPgpService.this, masterKeyId);
+ public String getCachedPassphrase(long masterKeyId) throws PgpDecryptVerify.NoSecretKeyException {
+ try {
+ return PassphraseCacheService.getCachedPassphrase(
+ OpenPgpService.this, masterKeyId);
+ } catch (PassphraseCacheService.KeyNotFoundException e) {
+ throw new PgpDecryptVerify.NoSecretKeyException();
+ }
}
},
inputData, os
);
- builder.setAllowSymmetricDecryption(false) // no support for symmetric encryption
- .setAllowedKeyIds(allowedKeyIds) // allow only private keys associated with
- // accounts of this app
- .setPassphrase(passphrase);
+
+ // allow only private keys associated with accounts of this app
+ // no support for symmetric encryption
+ builder.setPassphrase(passphrase)
+ .setAllowSymmetricDecryption(false)
+ .setAllowedKeyIds(allowedKeyIds)
+ .setDecryptMetadataOnly(decryptMetadataOnly);
PgpDecryptVerifyResult decryptVerifyResult;
try {
@@ -409,8 +435,7 @@ public class OpenPgpService extends RemoteService {
if (PgpDecryptVerifyResult.KEY_PASSHRASE_NEEDED == decryptVerifyResult.getStatus()) {
// get PendingIntent for passphrase input, add it to given params and return to client
return getPassphraseIntent(data, decryptVerifyResult.getKeyIdPassphraseNeeded());
- } else if (PgpDecryptVerifyResult.SYMMETRIC_PASSHRASE_NEEDED ==
- decryptVerifyResult.getStatus()) {
+ } else if (PgpDecryptVerifyResult.SYMMETRIC_PASSHRASE_NEEDED == decryptVerifyResult.getStatus()) {
throw new PgpGeneralException("Decryption of symmetric content not supported by API!");
}
@@ -434,9 +459,18 @@ public class OpenPgpService extends RemoteService {
}
}
+ if (data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) >= 4) {
+ OpenPgpMetadata metadata = decryptVerifyResult.getDecryptMetadata();
+ if (metadata != null) {
+ result.putExtra(OpenPgpApi.RESULT_METADATA, metadata);
+ }
+ }
+
} finally {
is.close();
- os.close();
+ if (os != null) {
+ os.close();
+ }
}
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
@@ -492,6 +526,7 @@ public class OpenPgpService extends RemoteService {
return result;
}
} catch (Exception e) {
+ Log.d(Constants.TAG, "getKeyImpl", e);
Intent result = new Intent();
result.putExtra(OpenPgpApi.RESULT_ERROR,
new OpenPgpError(OpenPgpError.GENERIC_ERROR, e.getMessage()));
@@ -537,10 +572,15 @@ public class OpenPgpService extends RemoteService {
}
// version code is required and needs to correspond to version code of service!
- if (data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) != OpenPgpApi.API_VERSION) {
+ // History of versions in org.openintents.openpgp.util.OpenPgpApi
+ // we support 3 and 4
+ if (data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) != 3
+ && data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) != 4) {
Intent result = new Intent();
OpenPgpError error = new OpenPgpError
- (OpenPgpError.INCOMPATIBLE_API_VERSIONS, "Incompatible API versions!");
+ (OpenPgpError.INCOMPATIBLE_API_VERSIONS, "Incompatible API versions!\n"
+ + "used API version: " + data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) + "\n"
+ + "supported API versions: 3, 4");
result.putExtra(OpenPgpApi.RESULT_ERROR, error);
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR);
return result;
@@ -588,7 +628,13 @@ public class OpenPgpService extends RemoteService {
Set<Long> allowedKeyIds =
mProviderHelper.getAllKeyIdsForApp(
ApiAccounts.buildBaseUri(currentPkg));
- return decryptAndVerifyImpl(data, input, output, allowedKeyIds);
+ return decryptAndVerifyImpl(data, input, output, allowedKeyIds, false);
+ } else if (OpenPgpApi.ACTION_DECRYPT_METADATA.equals(action)) {
+ String currentPkg = getCurrentCallingPackage();
+ Set<Long> allowedKeyIds =
+ mProviderHelper.getAllKeyIdsForApp(
+ ApiAccounts.buildBaseUri(currentPkg));
+ return decryptAndVerifyImpl(data, input, output, allowedKeyIds, true);
} else if (OpenPgpApi.ACTION_GET_KEY.equals(action)) {
return getKeyImpl(data);
} else if (OpenPgpApi.ACTION_GET_KEY_IDS.equals(action)) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java
index 8f1f46c04..666252353 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java
@@ -102,7 +102,7 @@ public class AccountSettingsActivity extends ActionBarActivity {
}
private void save() {
- new ProviderHelper(this).updateApiAccount(mAccountSettingsFragment.getAccSettings(), mAccountUri);
+ new ProviderHelper(this).updateApiAccount(mAccountUri, mAccountSettingsFragment.getAccSettings());
finish();
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java
index d0b958844..48c76d561 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java
@@ -18,11 +18,13 @@
package org.sufficientlysecure.keychain.remote.ui;
import android.content.Intent;
+import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
+import android.widget.TextView;
import org.openintents.openpgp.util.OpenPgpApi;
import org.sufficientlysecure.htmltextview.HtmlTextView;
@@ -37,6 +39,7 @@ import org.sufficientlysecure.keychain.ui.SelectPublicKeyFragment;
import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
import org.sufficientlysecure.keychain.util.Log;
+import java.security.Provider;
import java.util.ArrayList;
public class RemoteServiceActivity extends ActionBarActivity {
@@ -76,10 +79,17 @@ public class RemoteServiceActivity extends ActionBarActivity {
// select pub keys view
private SelectPublicKeyFragment mSelectFragment;
+ private ProviderHelper mProviderHelper;
+
+ // for ACTION_CREATE_ACCOUNT
+ boolean mUpdateExistingAccount;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ mProviderHelper = new ProviderHelper(this);
+
handleActions(getIntent(), savedInstanceState);
}
@@ -94,6 +104,14 @@ public class RemoteServiceActivity extends ActionBarActivity {
final byte[] packageSignature = extras.getByteArray(EXTRA_PACKAGE_SIGNATURE);
Log.d(Constants.TAG, "ACTION_REGISTER packageName: " + packageName);
+ setContentView(R.layout.api_remote_register_app);
+
+ mAppSettingsFragment = (AppSettingsFragment) getSupportFragmentManager().findFragmentById(
+ R.id.api_app_settings_fragment);
+
+ AppSettings settings = new AppSettings(packageName, packageSignature);
+ mAppSettingsFragment.setAppSettings(settings);
+
// Inflate a "Done"/"Cancel" custom action bar view
ActionBarHelper.setTwoButtonView(getSupportActionBar(),
R.string.api_register_allow, R.drawable.ic_action_done,
@@ -102,8 +120,7 @@ public class RemoteServiceActivity extends ActionBarActivity {
public void onClick(View v) {
// Allow
- new ProviderHelper(RemoteServiceActivity.this).insertApiApp(
- mAppSettingsFragment.getAppSettings());
+ mProviderHelper.insertApiApp(mAppSettingsFragment.getAppSettings());
// give data through for new service call
Intent resultData = extras.getParcelable(EXTRA_DATA);
@@ -120,18 +137,34 @@ public class RemoteServiceActivity extends ActionBarActivity {
}
}
);
-
- setContentView(R.layout.api_remote_register_app);
-
- mAppSettingsFragment = (AppSettingsFragment) getSupportFragmentManager().findFragmentById(
- R.id.api_app_settings_fragment);
-
- AppSettings settings = new AppSettings(packageName, packageSignature);
- mAppSettingsFragment.setAppSettings(settings);
} else if (ACTION_CREATE_ACCOUNT.equals(action)) {
final String packageName = extras.getString(EXTRA_PACKAGE_NAME);
final String accName = extras.getString(EXTRA_ACC_NAME);
+ setContentView(R.layout.api_remote_create_account);
+
+ mAccSettingsFragment = (AccountSettingsFragment) getSupportFragmentManager().findFragmentById(
+ R.id.api_account_settings_fragment);
+
+ TextView text = (TextView) findViewById(R.id.api_remote_create_account_text);
+
+ // update existing?
+ Uri uri = KeychainContract.ApiAccounts.buildByPackageAndAccountUri(packageName, accName);
+ AccountSettings settings = mProviderHelper.getApiAccountSettings(uri);
+ if (settings == null) {
+ // create new account
+ settings = new AccountSettings(accName);
+ mUpdateExistingAccount = false;
+
+ text.setText(R.string.api_create_account_text);
+ } else {
+ // update existing account
+ mUpdateExistingAccount = true;
+
+ text.setText(R.string.api_update_account_text);
+ }
+ mAccSettingsFragment.setAccSettings(settings);
+
// Inflate a "Done"/"Cancel" custom action bar view
ActionBarHelper.setTwoButtonView(getSupportActionBar(),
R.string.api_settings_save, R.drawable.ic_action_done,
@@ -145,9 +178,17 @@ public class RemoteServiceActivity extends ActionBarActivity {
mAccSettingsFragment.setErrorOnSelectKeyFragment(
getString(R.string.api_register_error_select_key));
} else {
- new ProviderHelper(RemoteServiceActivity.this).insertApiAccount(
- KeychainContract.ApiAccounts.buildBaseUri(packageName),
- mAccSettingsFragment.getAccSettings());
+ if (mUpdateExistingAccount) {
+ Uri baseUri = KeychainContract.ApiAccounts.buildBaseUri(packageName);
+ Uri accountUri = baseUri.buildUpon().appendEncodedPath(accName).build();
+ mProviderHelper.updateApiAccount(
+ accountUri,
+ mAccSettingsFragment.getAccSettings());
+ } else {
+ mProviderHelper.insertApiAccount(
+ KeychainContract.ApiAccounts.buildBaseUri(packageName),
+ mAccSettingsFragment.getAccSettings());
+ }
// give data through for new service call
Intent resultData = extras.getParcelable(EXTRA_DATA);
@@ -166,13 +207,6 @@ public class RemoteServiceActivity extends ActionBarActivity {
}
);
- setContentView(R.layout.api_remote_create_account);
-
- mAccSettingsFragment = (AccountSettingsFragment) getSupportFragmentManager().findFragmentById(
- R.id.api_account_settings_fragment);
-
- AccountSettings settings = new AccountSettings(accName);
- mAccSettingsFragment.setAccSettings(settings);
} else if (ACTION_CACHE_PASSPHRASE.equals(action)) {
long secretKeyId = extras.getLong(EXTRA_SECRET_KEY_ID);
final Intent resultData = extras.getParcelable(EXTRA_DATA);
@@ -190,7 +224,8 @@ public class RemoteServiceActivity extends ActionBarActivity {
RemoteServiceActivity.this.finish();
}
- });
+ }
+ );
} else if (ACTION_SELECT_PUB_KEYS.equals(action)) {
long[] selectedMasterKeyIds = intent.getLongArrayExtra(EXTRA_SELECTED_MASTER_KEY_IDS);
@@ -286,7 +321,8 @@ public class RemoteServiceActivity extends ActionBarActivity {
RemoteServiceActivity.this.setResult(RESULT_CANCELED);
RemoteServiceActivity.this.finish();
}
- });
+ }
+ );
setContentView(R.layout.api_remote_error_message);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
index a7115a53d..d6c470e11 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
@@ -86,6 +86,8 @@ public class KeychainIntentService extends IntentService
public static final String ACTION_DECRYPT_VERIFY = Constants.INTENT_PREFIX + "DECRYPT_VERIFY";
+ public static final String ACTION_DECRYPT_METADATA = Constants.INTENT_PREFIX + "DECRYPT_METADATA";
+
public static final String ACTION_SAVE_KEYRING = Constants.INTENT_PREFIX + "SAVE_KEYRING";
public static final String ACTION_DELETE_FILE_SECURELY = Constants.INTENT_PREFIX
@@ -241,16 +243,17 @@ public class KeychainIntentService extends IntentService
data.putInt(SELECTED_URI, i);
InputData inputData = createEncryptInputData(data);
OutputStream outStream = createCryptOutputStream(data);
+ String originalFilename = getOriginalFilename(data);
/* Operation */
PgpSignEncrypt.Builder builder =
new PgpSignEncrypt.Builder(
new ProviderHelper(this),
- PgpHelper.getFullVersion(this),
inputData, outStream);
builder.setProgressable(this);
builder.setEnableAsciiArmorOutput(useAsciiArmor)
+ .setVersionHeader(PgpHelper.getVersionForHeader(this))
.setCompressionId(compressionId)
.setSymmetricEncryptionAlgorithm(
Preferences.getPreferences(this).getDefaultEncryptionAlgorithm())
@@ -261,7 +264,8 @@ public class KeychainIntentService extends IntentService
.setSignatureHashAlgorithm(
Preferences.getPreferences(this).getDefaultHashAlgorithm())
.setSignaturePassphrase(
- PassphraseCacheService.getCachedPassphrase(this, signatureKeyId));
+ PassphraseCacheService.getCachedPassphrase(this, signatureKeyId))
+ .setOriginalFilename(originalFilename);
// this assumes that the bytes are cleartext (valid for current implementation!)
if (source == IO_BYTES) {
@@ -302,15 +306,19 @@ public class KeychainIntentService extends IntentService
new ProviderHelper(this),
new PgpDecryptVerify.PassphraseCache() {
@Override
- public String getCachedPassphrase(long masterKeyId) {
- return PassphraseCacheService.getCachedPassphrase(
- KeychainIntentService.this, masterKeyId);
+ public String getCachedPassphrase(long masterKeyId) throws PgpDecryptVerify.NoSecretKeyException {
+ try {
+ return PassphraseCacheService.getCachedPassphrase(
+ KeychainIntentService.this, masterKeyId);
+ } catch (PassphraseCacheService.KeyNotFoundException e) {
+ throw new PgpDecryptVerify.NoSecretKeyException();
+ }
}
},
- inputData, outStream);
- builder.setProgressable(this);
-
- builder.setAllowSymmetricDecryption(true)
+ inputData, outStream
+ );
+ builder.setProgressable(this)
+ .setAllowSymmetricDecryption(true)
.setPassphrase(passphrase);
PgpDecryptVerifyResult decryptVerifyResult = builder.build().execute();
@@ -329,6 +337,50 @@ public class KeychainIntentService extends IntentService
} catch (Exception e) {
sendErrorToHandler(e);
}
+ } else if (ACTION_DECRYPT_METADATA.equals(action)) {
+ try {
+ /* Input */
+ String passphrase = data.getString(DECRYPT_PASSPHRASE);
+
+ InputData inputData = createDecryptInputData(data);
+
+ /* Operation */
+
+ Bundle resultData = new Bundle();
+
+ // verifyText and decrypt returning additional resultData values for the
+ // verification of signatures
+ PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(
+ new ProviderHelper(this),
+ new PgpDecryptVerify.PassphraseCache() {
+ @Override
+ public String getCachedPassphrase(long masterKeyId) throws PgpDecryptVerify.NoSecretKeyException {
+ try {
+ return PassphraseCacheService.getCachedPassphrase(
+ KeychainIntentService.this, masterKeyId);
+ } catch (PassphraseCacheService.KeyNotFoundException e) {
+ throw new PgpDecryptVerify.NoSecretKeyException();
+ }
+ }
+ },
+ inputData, null
+ );
+ builder.setProgressable(this)
+ .setAllowSymmetricDecryption(true)
+ .setPassphrase(passphrase)
+ .setDecryptMetadataOnly(true);
+
+ PgpDecryptVerifyResult decryptVerifyResult = builder.build().execute();
+
+ resultData.putParcelable(RESULT_DECRYPT_VERIFY_RESULT, decryptVerifyResult);
+
+ /* Output */
+ OtherHelper.logDebugBundle(resultData, "resultData");
+
+ sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData);
+ } catch (Exception e) {
+ sendErrorToHandler(e);
+ }
} else if (ACTION_SAVE_KEYRING.equals(action)) {
try {
/* Input */
@@ -355,7 +407,7 @@ public class KeychainIntentService extends IntentService
UncachedKeyRing ring = result.getRing();
- providerHelper.saveSecretKeyRing(ring, new ProgressScaler(this, 60, 95, 100));
+ providerHelper.saveSecretKeyRing(ring, new ProgressScaler(this, 60, 95, 100));
// cache new passphrase
if (saveParcel.mNewPassphrase != null) {
@@ -402,7 +454,7 @@ public class KeychainIntentService extends IntentService
} else {
// get entries from cached file
FileImportCache<ParcelableKeyRing> cache =
- new FileImportCache<ParcelableKeyRing>(this);
+ new FileImportCache<ParcelableKeyRing>(this);
entries = cache.readCacheIntoList();
}
@@ -575,7 +627,7 @@ public class KeychainIntentService extends IntentService
CanonicalizedPublicKeyRing publicRing = providerHelper.getCanonicalizedPublicKeyRing(pubKeyId);
CanonicalizedSecretKeyRing secretKeyRing = providerHelper.getCanonicalizedSecretKeyRing(masterKeyId);
CanonicalizedSecretKey certificationKey = secretKeyRing.getSecretKey();
- if(!certificationKey.unlock(signaturePassphrase)) {
+ if (!certificationKey.unlock(signaturePassphrase)) {
throw new PgpGeneralException("Error extracting key (bad passphrase?)");
}
UncachedKeyRing newRing = certificationKey.certifyUserIds(publicRing, userIds);
@@ -728,6 +780,27 @@ public class KeychainIntentService extends IntentService
}
}
+ private String getOriginalFilename(Bundle data) throws PgpGeneralException, FileNotFoundException {
+ int target = data.getInt(TARGET);
+ switch (target) {
+ case IO_BYTES:
+ return "";
+
+ case IO_URI:
+ Uri providerUri = data.getParcelable(ENCRYPT_INPUT_URI);
+
+ return FileHelper.getFilename(this, providerUri);
+
+ case IO_URIS:
+ providerUri = data.<Uri>getParcelableArrayList(ENCRYPT_INPUT_URIS).get(data.getInt(SELECTED_URI));
+
+ return FileHelper.getFilename(this, providerUri);
+
+ default:
+ throw new PgpGeneralException("No target choosen!");
+ }
+ }
+
private OutputStream createCryptOutputStream(Bundle data) throws PgpGeneralException, FileNotFoundException {
int target = data.getInt(TARGET);
switch (target) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java
index e813ca26c..8cd9876eb 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java
@@ -77,12 +77,24 @@ public class PassphraseCacheService extends Service {
private static final int NOTIFICATION_ID = 1;
+ private static final int MSG_PASSPHRASE_CACHE_GET_OKAY = 1;
+ private static final int MSG_PASSPHRASE_CACHE_GET_KEY_NO_FOUND = 2;
+
private BroadcastReceiver mIntentReceiver;
private LongSparseArray<CachedPassphrase> mPassphraseCache = new LongSparseArray<CachedPassphrase>();
Context mContext;
+ public static class KeyNotFoundException extends Exception {
+ public KeyNotFoundException() {
+ }
+
+ public KeyNotFoundException(String name) {
+ super(name);
+ }
+ }
+
/**
* This caches a new passphrase in memory by sending a new command to the service. An android
* service is only run once. Thus, when the service is already started, new commands just add
@@ -115,24 +127,23 @@ public class PassphraseCacheService extends Service {
* @param keyId
* @return passphrase or null (if no passphrase is cached for this keyId)
*/
- public static String getCachedPassphrase(Context context, long keyId) {
+ public static String getCachedPassphrase(Context context, long keyId) throws KeyNotFoundException {
Log.d(Constants.TAG, "PassphraseCacheService.getCachedPassphrase() get masterKeyId for " + keyId);
Intent intent = new Intent(context, PassphraseCacheService.class);
intent.setAction(ACTION_PASSPHRASE_CACHE_GET);
final Object mutex = new Object();
- final Bundle returnBundle = new Bundle();
+ final Message returnMessage = Message.obtain();
HandlerThread handlerThread = new HandlerThread("getPassphraseThread");
handlerThread.start();
Handler returnHandler = new Handler(handlerThread.getLooper()) {
@Override
public void handleMessage(Message message) {
- if (message.obj != null) {
- String passphrase = ((Bundle) message.obj).getString(EXTRA_PASSPHRASE);
- returnBundle.putString(EXTRA_PASSPHRASE, passphrase);
- }
+ // copy over result to handle after mutex.wait
+ returnMessage.what = message.what;
+ returnMessage.copyFrom(message);
synchronized (mutex) {
mutex.notify();
}
@@ -156,10 +167,13 @@ public class PassphraseCacheService extends Service {
}
}
- if (returnBundle.containsKey(EXTRA_PASSPHRASE)) {
- return returnBundle.getString(EXTRA_PASSPHRASE);
- } else {
- return null;
+ switch (returnMessage.what) {
+ case MSG_PASSPHRASE_CACHE_GET_OKAY:
+ return returnMessage.getData().getString(EXTRA_PASSPHRASE);
+ case MSG_PASSPHRASE_CACHE_GET_KEY_NO_FOUND:
+ throw new KeyNotFoundException();
+ default:
+ throw new KeyNotFoundException("should not happen!");
}
}
@@ -169,7 +183,7 @@ public class PassphraseCacheService extends Service {
* @param keyId
* @return
*/
- private String getCachedPassphraseImpl(long keyId) {
+ private String getCachedPassphraseImpl(long keyId) throws ProviderHelper.NotFoundException {
// passphrase for symmetric encryption?
if (keyId == Constants.key.symmetric) {
Log.d(Constants.TAG, "PassphraseCacheService.getCachedPassphraseImpl() for symmetric encryption");
@@ -182,46 +196,41 @@ public class PassphraseCacheService extends Service {
}
// try to get master key id which is used as an identifier for cached passphrases
- try {
- Log.d(Constants.TAG, "PassphraseCacheService.getCachedPassphraseImpl() for masterKeyId " + keyId);
- CanonicalizedSecretKeyRing key = new ProviderHelper(this).getCanonicalizedSecretKeyRing(
- KeychainContract.KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(keyId));
- // no passphrase needed? just add empty string and return it, then
- if (!key.hasPassphrase()) {
- Log.d(Constants.TAG, "Key has no passphrase! Caches and returns empty passphrase!");
+ Log.d(Constants.TAG, "PassphraseCacheService.getCachedPassphraseImpl() for masterKeyId " + keyId);
+ CanonicalizedSecretKeyRing key = new ProviderHelper(this).getCanonicalizedSecretKeyRing(
+ KeychainContract.KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(keyId));
- try {
- addCachedPassphrase(this, keyId, "", key.getPrimaryUserIdWithFallback());
- } catch (PgpGeneralException e) {
- Log.d(Constants.TAG, "PgpGeneralException occured");
- }
- return "";
- }
+ // no passphrase needed? just add empty string and return it, then
+ if (!key.hasPassphrase()) {
+ Log.d(Constants.TAG, "Key has no passphrase! Caches and returns empty passphrase!");
- // TODO: HACK
+ // TODO: HACK for yubikeys
if (key.getSecretKey().getSecretKey().getS2K().getType() == S2K.GNU_DUMMY_S2K
&& key.getSecretKey().getSecretKey().getS2K().getProtectionMode() == 2) {
// NFC!
return "123456";
}
- // get cached passphrase
- CachedPassphrase cachedPassphrase = mPassphraseCache.get(keyId);
- if (cachedPassphrase == null) {
- Log.d(Constants.TAG, "PassphraseCacheService: Passphrase not (yet) cached, returning null");
- // not really an error, just means the passphrase is not cached but not empty either
- return null;
+ try {
+ addCachedPassphrase(this, keyId, "", key.getPrimaryUserIdWithFallback());
+ } catch (PgpGeneralException e) {
+ Log.d(Constants.TAG, "PgpGeneralException occured");
}
+ return "";
+ }
- // set it again to reset the cache life cycle
- Log.d(Constants.TAG, "PassphraseCacheService: Cache passphrase again when getting it!");
- addCachedPassphrase(this, keyId, cachedPassphrase.getPassphrase(), cachedPassphrase.getPrimaryUserID());
- return cachedPassphrase.getPassphrase();
-
- } catch (ProviderHelper.NotFoundException e) {
- Log.e(Constants.TAG, "PassphraseCacheService: Passphrase for unknown key was requested!");
+ // get cached passphrase
+ CachedPassphrase cachedPassphrase = mPassphraseCache.get(keyId);
+ if (cachedPassphrase == null) {
+ Log.d(Constants.TAG, "PassphraseCacheService: Passphrase not (yet) cached, returning null");
+ // not really an error, just means the passphrase is not cached but not empty either
return null;
}
+
+ // set it again to reset the cache life cycle
+ Log.d(Constants.TAG, "PassphraseCacheService: Cache passphrase again when getting it!");
+ addCachedPassphrase(this, keyId, cachedPassphrase.getPassphrase(), cachedPassphrase.getPrimaryUserID());
+ return cachedPassphrase.getPassphrase();
}
/**
@@ -303,12 +312,19 @@ public class PassphraseCacheService extends Service {
long keyId = intent.getLongExtra(EXTRA_KEY_ID, -1);
Messenger messenger = intent.getParcelableExtra(EXTRA_MESSENGER);
- String passphrase = getCachedPassphraseImpl(keyId);
Message msg = Message.obtain();
- Bundle bundle = new Bundle();
- bundle.putString(EXTRA_PASSPHRASE, passphrase);
- msg.obj = bundle;
+ try {
+ String passphrase = getCachedPassphraseImpl(keyId);
+ msg.what = MSG_PASSPHRASE_CACHE_GET_OKAY;
+ Bundle bundle = new Bundle();
+ bundle.putString(EXTRA_PASSPHRASE, passphrase);
+ msg.setData(bundle);
+ } catch (ProviderHelper.NotFoundException e) {
+ Log.e(Constants.TAG, "PassphraseCacheService: Passphrase for unknown key was requested!");
+ msg.what = MSG_PASSPHRASE_CACHE_GET_KEY_NO_FOUND;
+ }
+
try {
messenger.send(msg);
} catch (RemoteException e) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java
index 467e0c14a..c1986825c 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java
@@ -231,7 +231,14 @@ public class CertifyKeyActivity extends ActionBarActivity implements
*/
private void initiateCertifying() {
// get the user's passphrase for this key (if required)
- String passphrase = PassphraseCacheService.getCachedPassphrase(this, mMasterKeyId);
+ String passphrase = null;
+ try {
+ passphrase = PassphraseCacheService.getCachedPassphrase(this, mMasterKeyId);
+ } catch (PassphraseCacheService.KeyNotFoundException e) {
+ Log.e(Constants.TAG, "Key not found!", e);
+ finish();
+ return;
+ }
if (passphrase == null) {
PassphraseDialogFragment.show(this, mMasterKeyId,
new Handler() {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java
index c33b1489a..845fbfa3b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java
@@ -23,8 +23,10 @@ import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
+import android.os.Handler;
import android.os.Message;
import android.os.Messenger;
+import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -38,6 +40,7 @@ import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment;
+import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Notify;
@@ -113,7 +116,8 @@ public class DecryptFileFragment extends DecryptFragment {
return;
}
- askForOutputFilename();
+// askForOutputFilename();
+ decryptOriginalFilename(null);
}
private String removeEncryptedAppend(String name) {
@@ -123,8 +127,13 @@ public class DecryptFileFragment extends DecryptFragment {
return name;
}
- private void askForOutputFilename() {
- String targetName = removeEncryptedAppend(FileHelper.getFilename(getActivity(), mInputUri));
+ private void askForOutputFilename(String originalFilename) {
+ String targetName;
+ if (!TextUtils.isEmpty(originalFilename)) {
+ targetName = originalFilename;
+ } else {
+ targetName = removeEncryptedAppend(FileHelper.getFilename(getActivity(), mInputUri));
+ }
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
File file = new File(mInputUri.getPath());
File parentDir = file.exists() ? file.getParentFile() : Constants.Path.APP_DIR;
@@ -136,6 +145,82 @@ public class DecryptFileFragment extends DecryptFragment {
}
}
+ private void decryptOriginalFilename(String passphrase) {
+ Log.d(Constants.TAG, "decryptOriginalFilename");
+
+ Intent intent = new Intent(getActivity(), KeychainIntentService.class);
+
+ // fill values for this action
+ Bundle data = new Bundle();
+ intent.setAction(KeychainIntentService.ACTION_DECRYPT_METADATA);
+
+ // data
+ Log.d(Constants.TAG, "mInputUri=" + mInputUri + ", mOutputUri=" + mOutputUri);
+
+ data.putInt(KeychainIntentService.SOURCE, KeychainIntentService.IO_URI);
+ data.putParcelable(KeychainIntentService.ENCRYPT_INPUT_URI, mInputUri);
+
+ data.putInt(KeychainIntentService.TARGET, KeychainIntentService.IO_URI);
+ data.putParcelable(KeychainIntentService.ENCRYPT_OUTPUT_URI, mOutputUri);
+
+ data.putString(KeychainIntentService.DECRYPT_PASSPHRASE, passphrase);
+
+ intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
+
+ // Message is received after decrypting is done in KeychainIntentService
+ KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(getActivity(),
+ getString(R.string.progress_decrypting), ProgressDialog.STYLE_HORIZONTAL) {
+ public void handleMessage(Message message) {
+ // handle messages by standard KeychainIntentServiceHandler first
+ super.handleMessage(message);
+
+ if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
+ // get returned data bundle
+ Bundle returnData = message.getData();
+
+ PgpDecryptVerifyResult decryptVerifyResult =
+ returnData.getParcelable(KeychainIntentService.RESULT_DECRYPT_VERIFY_RESULT);
+
+ if (PgpDecryptVerifyResult.KEY_PASSHRASE_NEEDED == decryptVerifyResult.getStatus()) {
+ showPassphraseDialogForFilename(decryptVerifyResult.getKeyIdPassphraseNeeded());
+ } else if (PgpDecryptVerifyResult.SYMMETRIC_PASSHRASE_NEEDED ==
+ decryptVerifyResult.getStatus()) {
+ showPassphraseDialogForFilename(Constants.key.symmetric);
+ } else {
+
+ // go on...
+ askForOutputFilename(decryptVerifyResult.getDecryptMetadata().getFilename());
+ }
+ }
+ }
+ };
+
+ // Create a new Messenger for the communication back
+ Messenger messenger = new Messenger(saveHandler);
+ intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
+
+ // show progress dialog
+ saveHandler.showProgressDialog(getActivity());
+
+ // start service with intent
+ getActivity().startService(intent);
+ }
+
+ protected void showPassphraseDialogForFilename(long keyId) {
+ PassphraseDialogFragment.show(getActivity(), keyId,
+ new Handler() {
+ @Override
+ public void handleMessage(Message message) {
+ if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
+ String passphrase =
+ message.getData().getString(PassphraseDialogFragment.MESSAGE_DATA_PASSPHRASE);
+ decryptOriginalFilename(passphrase);
+ }
+ }
+ }
+ );
+ }
+
@Override
protected void decryptStart(String passphrase) {
Log.d(Constants.TAG, "decryptStart");
@@ -161,7 +246,7 @@ public class DecryptFileFragment extends DecryptFragment {
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
- // Message is received after encrypting is done in KeychainIntentService
+ // Message is received after decrypting is done in KeychainIntentService
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(getActivity(),
getString(R.string.progress_decrypting), ProgressDialog.STYLE_HORIZONTAL) {
public void handleMessage(Message message) {
@@ -178,7 +263,7 @@ public class DecryptFileFragment extends DecryptFragment {
if (PgpDecryptVerifyResult.KEY_PASSHRASE_NEEDED == decryptVerifyResult.getStatus()) {
showPassphraseDialog(decryptVerifyResult.getKeyIdPassphraseNeeded());
} else if (PgpDecryptVerifyResult.SYMMETRIC_PASSHRASE_NEEDED ==
- decryptVerifyResult.getStatus()) {
+ decryptVerifyResult.getStatus()) {
showPassphraseDialog(Constants.key.symmetric);
} else {
// display signature result in activity
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java
index 5be196a45..03074bb6a 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java
@@ -339,6 +339,10 @@ public class EditKeyFragment extends LoaderFragment implements
mSaveKeyringParcel.mRevokeUserIds.remove(userId);
} else {
mSaveKeyringParcel.mRevokeUserIds.add(userId);
+ // not possible to revoke and change to primary user id
+ if (mSaveKeyringParcel.mChangePrimaryUserId.equals(userId)) {
+ mSaveKeyringParcel.mChangePrimaryUserId = null;
+ }
}
break;
}
@@ -471,8 +475,14 @@ public class EditKeyFragment extends LoaderFragment implements
}
private void cachePassphraseForEdit() {
- mCurrentPassphrase = PassphraseCacheService.getCachedPassphrase(getActivity(),
- mSaveKeyringParcel.mMasterKeyId);
+ try {
+ mCurrentPassphrase = PassphraseCacheService.getCachedPassphrase(getActivity(),
+ mSaveKeyringParcel.mMasterKeyId);
+ } catch (PassphraseCacheService.KeyNotFoundException e) {
+ Log.e(Constants.TAG, "Key not found!", e);
+ getActivity().finish();
+ return;
+ }
if (mCurrentPassphrase == null) {
PassphraseDialogFragment.show(getActivity(), mSaveKeyringParcel.mMasterKeyId,
new Handler() {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java
index 94f828b48..7e08f6b7c 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java
@@ -81,10 +81,6 @@ public class EncryptActivity extends DrawerActivity implements EncryptActivityIn
PagerTabStripAdapter mTabsAdapterContent;
// tabs
- Bundle mAsymmetricFragmentBundle = new Bundle();
- Bundle mSymmetricFragmentBundle = new Bundle();
- Bundle mMessageFragmentBundle = new Bundle();
- Bundle mFileFragmentBundle = new Bundle();
int mSwitchToMode = PAGER_MODE_ASYMMETRIC;
int mSwitchToContent = PAGER_CONTENT_MESSAGE;
@@ -93,7 +89,7 @@ public class EncryptActivity extends DrawerActivity implements EncryptActivityIn
private static final int PAGER_CONTENT_MESSAGE = 0;
private static final int PAGER_CONTENT_FILE = 1;
- // model used by message and file fragments
+ // model used by fragments
private long mEncryptionKeyIds[] = null;
private String mEncryptionUserIds[] = null;
private long mSigningKeyId = Constants.key.none;
@@ -267,21 +263,23 @@ public class EncryptActivity extends DrawerActivity implements EncryptActivityIn
if (isContentMessage()) {
data.putInt(KeychainIntentService.TARGET, KeychainIntentService.IO_BYTES);
data.putByteArray(KeychainIntentService.ENCRYPT_MESSAGE_BYTES, mMessage.getBytes());
+
+ data.putInt(KeychainIntentService.ENCRYPT_COMPRESSION_ID,
+ Preferences.getPreferences(this).getDefaultMessageCompression());
} else {
data.putInt(KeychainIntentService.SOURCE, KeychainIntentService.IO_URIS);
data.putParcelableArrayList(KeychainIntentService.ENCRYPT_INPUT_URIS, mInputUris);
data.putInt(KeychainIntentService.TARGET, KeychainIntentService.IO_URIS);
data.putParcelableArrayList(KeychainIntentService.ENCRYPT_OUTPUT_URIS, mOutputUris);
+
+ data.putInt(KeychainIntentService.ENCRYPT_COMPRESSION_ID,
+ Preferences.getPreferences(this).getDefaultFileCompression());
}
// Always use armor for messages
data.putBoolean(KeychainIntentService.ENCRYPT_USE_ASCII_ARMOR, mUseArmor || isContentMessage());
- // TODO: Only default compression right now...
- int compressionId = Preferences.getPreferences(this).getDefaultMessageCompression();
- data.putInt(KeychainIntentService.ENCRYPT_COMPRESSION_ID, compressionId);
-
if (isModeSymmetric()) {
Log.d(Constants.TAG, "Symmetric encryption enabled!");
String passphrase = mPassphrase;
@@ -426,7 +424,6 @@ public class EncryptActivity extends DrawerActivity implements EncryptActivityIn
if (isModeSymmetric()) {
// symmetric encryption checks
-
if (mPassphrase == null) {
Notify.showNotify(this, R.string.passphrases_do_not_match, Notify.Style.ERROR);
return false;
@@ -453,20 +450,24 @@ public class EncryptActivity extends DrawerActivity implements EncryptActivityIn
return false;
}
- if (mSigningKeyId != 0 && PassphraseCacheService.getCachedPassphrase(this, mSigningKeyId) == null) {
- PassphraseDialogFragment.show(this, mSigningKeyId,
- new Handler() {
- @Override
- public void handleMessage(Message message) {
- if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
- // restart
- startEncrypt();
+ try {
+ if (mSigningKeyId != 0 && PassphraseCacheService.getCachedPassphrase(this, mSigningKeyId) == null) {
+ PassphraseDialogFragment.show(this, mSigningKeyId,
+ new Handler() {
+ @Override
+ public void handleMessage(Message message) {
+ if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
+ // restart
+ startEncrypt();
+ }
}
}
- }
- );
+ );
- return false;
+ return false;
+ }
+ } catch (PassphraseCacheService.KeyNotFoundException e) {
+ Log.e(Constants.TAG, "Key not found!", e);
}
}
return true;
@@ -502,16 +503,12 @@ public class EncryptActivity extends DrawerActivity implements EncryptActivityIn
// Handle intent actions
handleActions(getIntent());
- mTabsAdapterMode.addTab(EncryptAsymmetricFragment.class,
- mAsymmetricFragmentBundle, getString(R.string.label_asymmetric));
- mTabsAdapterMode.addTab(EncryptSymmetricFragment.class,
- mSymmetricFragmentBundle, getString(R.string.label_symmetric));
+ mTabsAdapterMode.addTab(EncryptAsymmetricFragment.class, null, getString(R.string.label_asymmetric));
+ mTabsAdapterMode.addTab(EncryptSymmetricFragment.class, null, getString(R.string.label_symmetric));
mViewPagerMode.setCurrentItem(mSwitchToMode);
- mTabsAdapterContent.addTab(EncryptMessageFragment.class,
- mMessageFragmentBundle, getString(R.string.label_message));
- mTabsAdapterContent.addTab(EncryptFileFragment.class,
- mFileFragmentBundle, getString(R.string.label_files));
+ mTabsAdapterContent.addTab(EncryptMessageFragment.class, null, getString(R.string.label_message));
+ mTabsAdapterContent.addTab(EncryptFileFragment.class, null, getString(R.string.label_files));
mViewPagerContent.setCurrentItem(mSwitchToContent);
mUseArmor = Preferences.getPreferences(this).getDefaultAsciiArmor();
@@ -603,14 +600,10 @@ public class EncryptActivity extends DrawerActivity implements EncryptActivityIn
String textData = extras.getString(EXTRA_TEXT);
- long signatureKeyId = extras.getLong(EXTRA_SIGNATURE_KEY_ID);
- long[] encryptionKeyIds = extras.getLongArray(EXTRA_ENCRYPTION_KEY_IDS);
+ mSigningKeyId = extras.getLong(EXTRA_SIGNATURE_KEY_ID);
+ mEncryptionKeyIds = extras.getLongArray(EXTRA_ENCRYPTION_KEY_IDS);
// preselect keys given by intent
- mAsymmetricFragmentBundle.putLongArray(EncryptAsymmetricFragment.ARG_ENCRYPTION_KEY_IDS,
- encryptionKeyIds);
- mAsymmetricFragmentBundle.putLong(EncryptAsymmetricFragment.ARG_SIGNATURE_KEY_ID,
- signatureKeyId);
mSwitchToMode = PAGER_MODE_ASYMMETRIC;
/**
@@ -618,11 +611,11 @@ public class EncryptActivity extends DrawerActivity implements EncryptActivityIn
*/
if (ACTION_ENCRYPT.equals(action) && textData != null) {
// encrypt text based on given extra
- mMessageFragmentBundle.putString(EncryptMessageFragment.ARG_TEXT, textData);
+ mMessage = textData;
mSwitchToContent = PAGER_CONTENT_MESSAGE;
} else if (ACTION_ENCRYPT.equals(action) && uris != null && !uris.isEmpty()) {
// encrypt file based on Uri
- mFileFragmentBundle.putParcelableArrayList(EncryptFileFragment.ARG_URIS, uris);
+ mInputUris = uris;
mSwitchToContent = PAGER_CONTENT_FILE;
} else if (ACTION_ENCRYPT.equals(action)) {
Log.e(Constants.TAG,
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java
index 3de617ca0..a402b6f68 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java
@@ -123,14 +123,10 @@ public class EncryptAsymmetricFragment extends Fragment implements EncryptActivi
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
-
- long signatureKeyId = getArguments().getLong(ARG_SIGNATURE_KEY_ID);
- long[] encryptionKeyIds = getArguments().getLongArray(ARG_ENCRYPTION_KEY_IDS);
-
mProviderHelper = new ProviderHelper(getActivity());
- // preselect keys given by arguments (given by Intent to EncryptActivity)
- preselectKeys(signatureKeyId, encryptionKeyIds, mProviderHelper);
+ // preselect keys given
+ preselectKeys();
getLoaderManager().initLoader(1, null, new LoaderManager.LoaderCallbacks<Cursor>() {
@Override
@@ -145,24 +141,17 @@ public class EncryptAsymmetricFragment extends Fragment implements EncryptActivi
KeyRings.MASTER_KEY_ID,
KeyRings.KEY_ID,
KeyRings.USER_ID,
- KeyRings.EXPIRY,
- KeyRings.IS_REVOKED,
- // can certify info only related to master key
- KeyRings.CAN_CERTIFY,
- // has sign may be any subkey
+ KeyRings.IS_EXPIRED,
KeyRings.HAS_SIGN,
- KeyRings.HAS_ANY_SECRET,
- KeyRings.HAS_SECRET
+ KeyRings.HAS_ANY_SECRET
};
- String where = KeyRings.HAS_ANY_SECRET + " = 1";
+ String where = KeyRings.HAS_ANY_SECRET + " = 1 AND " + KeyRings.HAS_SIGN + " NOT NULL AND "
+ + KeyRings.IS_REVOKED + " = 0 AND " + KeyRings.IS_EXPIRED + " = 0";
// Now create and return a CursorLoader that will take care of
// creating a Cursor for the data being displayed.
return new CursorLoader(getActivity(), baseUri, projection, where, null, null);
- /*return new CursorLoader(getActivity(), KeyRings.buildUnifiedKeyRingsUri(),
- new String[]{KeyRings.USER_ID, KeyRings.KEY_ID, KeyRings.MASTER_KEY_ID, KeyRings.HAS_ANY_SECRET}, SIGN_KEY_SELECTION,
- null, null);*/
}
@Override
@@ -194,19 +183,15 @@ public class EncryptAsymmetricFragment extends Fragment implements EncryptActivi
/**
* If an Intent gives a signatureMasterKeyId and/or encryptionMasterKeyIds, preselect those!
- *
- * @param preselectedSignatureKeyId
- * @param preselectedEncryptionKeyIds
*/
- private void preselectKeys(long preselectedSignatureKeyId, long[] preselectedEncryptionKeyIds,
- ProviderHelper providerHelper) {
+ private void preselectKeys() {
// TODO all of this works under the assumption that the first suitable subkey is always used!
// not sure if we need to distinguish between different subkeys here?
- if (preselectedSignatureKeyId != 0) {
+ long signatureKey = mEncryptInterface.getSignatureKey();
+ if (signatureKey != Constants.key.none) {
try {
- CachedPublicKeyRing keyring =
- providerHelper.getCachedPublicKeyRing(
- KeyRings.buildUnifiedKeyRingUri(preselectedSignatureKeyId));
+ CachedPublicKeyRing keyring = mProviderHelper.getCachedPublicKeyRing(
+ KeyRings.buildUnifiedKeyRingUri(signatureKey));
if(keyring.hasAnySecret()) {
setSignatureKeyId(keyring.getMasterKeyId());
}
@@ -215,10 +200,11 @@ public class EncryptAsymmetricFragment extends Fragment implements EncryptActivi
}
}
- if (preselectedEncryptionKeyIds != null) {
- for (long preselectedId : preselectedEncryptionKeyIds) {
+ long[] encryptionKeyIds = mEncryptInterface.getEncryptionKeys();
+ if (encryptionKeyIds != null) {
+ for (long preselectedId : encryptionKeyIds) {
try {
- CachedPublicKeyRing ring = providerHelper.getCachedPublicKeyRing(
+ CachedPublicKeyRing ring = mProviderHelper.getCachedPublicKeyRing(
KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(preselectedId));
mEncryptKeyView.addObject(mEncryptKeyView.new EncryptionKey(ring));
} catch (PgpGeneralException e) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFileFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFileFragment.java
index 8a9e17020..14d3d1c4a 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFileFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFileFragment.java
@@ -111,8 +111,6 @@ public class EncryptFileFragment extends Fragment implements EncryptActivityInte
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
-
- addInputUris(getArguments().<Uri>getParcelableArrayList(ARG_URIS));
}
private void addInputUri() {
@@ -125,14 +123,6 @@ public class EncryptFileFragment extends Fragment implements EncryptActivityInte
}
}
- private void addInputUris(List<Uri> uris) {
- if (uris != null) {
- for (Uri uri : uris) {
- addInputUri(uri);
- }
- }
- }
-
private void addInputUri(Uri inputUri) {
if (inputUri == null) {
return;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java
index a6561dfad..de3236d35 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java
@@ -119,8 +119,8 @@ public class PreferencesActivity extends PreferenceActivity {
initializeAsciiArmor(
(CheckBoxPreference) findPreference(Constants.Pref.DEFAULT_ASCII_ARMOR));
- initializeConcealPgpApplication(
- (CheckBoxPreference) findPreference(Constants.Pref.CONCEAL_PGP_APPLICATION));
+ initializeWriteVersionHeader(
+ (CheckBoxPreference) findPreference(Constants.Pref.WRITE_VERSION_HEADER));
} else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
// Load the legacy preferences headers
@@ -263,8 +263,8 @@ public class PreferencesActivity extends PreferenceActivity {
initializeAsciiArmor(
(CheckBoxPreference) findPreference(Constants.Pref.DEFAULT_ASCII_ARMOR));
- initializeConcealPgpApplication(
- (CheckBoxPreference) findPreference(Constants.Pref.CONCEAL_PGP_APPLICATION));
+ initializeWriteVersionHeader(
+ (CheckBoxPreference) findPreference(Constants.Pref.WRITE_VERSION_HEADER));
}
}
@@ -386,12 +386,12 @@ public class PreferencesActivity extends PreferenceActivity {
});
}
- private static void initializeConcealPgpApplication(final CheckBoxPreference mConcealPgpApplication) {
- mConcealPgpApplication.setChecked(sPreferences.getConcealPgpApplication());
- mConcealPgpApplication.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
+ private static void initializeWriteVersionHeader(final CheckBoxPreference mWriteVersionHeader) {
+ mWriteVersionHeader.setChecked(sPreferences.getWriteVersionHeader());
+ mWriteVersionHeader.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
public boolean onPreferenceChange(Preference preference, Object newValue) {
- mConcealPgpApplication.setChecked((Boolean) newValue);
- sPreferences.setConcealPgpApplication((Boolean) newValue);
+ mWriteVersionHeader.setChecked((Boolean) newValue);
+ sPreferences.setWriteVersionHeader((Boolean) newValue);
return false;
}
});
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java
index 370a7312f..08243f06b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java
@@ -22,17 +22,22 @@ import android.database.Cursor;
import android.graphics.PorterDuff;
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.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.AdapterView;
import android.widget.ImageView;
import android.widget.ListView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract;
@@ -41,6 +46,8 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException;
import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter;
+import org.sufficientlysecure.keychain.ui.dialog.EditSubkeyDialogFragment;
+import org.sufficientlysecure.keychain.ui.dialog.UserIdInfoDialogFragment;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Notify;
@@ -88,9 +95,30 @@ public class ViewKeyMainFragment extends LoaderFragment implements
PorterDuff.Mode.SRC_IN);
mActionUpdate = view.findViewById(R.id.view_key_action_update);
+ mUserIds.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ showUserIdInfo(position);
+ }
+ });
+
return root;
}
+ private void showUserIdInfo(final int position) {
+ final boolean isRevoked = mUserIdsAdapter.getIsRevoked(position);
+ final int isVerified = mUserIdsAdapter.getIsVerified(position);
+
+ DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() {
+ public void run() {
+ UserIdInfoDialogFragment dialogFragment =
+ UserIdInfoDialogFragment.newInstance(isRevoked, isVerified);
+
+ dialogFragment.show(getActivity().getSupportFragmentManager(), "userIdInfoDialog");
+ }
+ });
+ }
+
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java
index 9bf47a387..717dcf818 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java
@@ -257,6 +257,11 @@ public class UserIdsAdapter extends CursorAdapter implements AdapterView.OnItemC
return mCursor.getInt(INDEX_IS_REVOKED) > 0;
}
+ public int getIsVerified(int position) {
+ mCursor.moveToPosition(position);
+ return mCursor.getInt(INDEX_VERIFIED);
+ }
+
public boolean getIsRevokedPending(int position) {
mCursor.moveToPosition(position);
String userId = mCursor.getString(INDEX_USER_ID);
@@ -280,24 +285,4 @@ public class UserIdsAdapter extends CursorAdapter implements AdapterView.OnItemC
return view;
}
- // Disable selection of items for lists without checkboxes, http://stackoverflow.com/a/4075045
- @Override
- public boolean areAllItemsEnabled() {
- if (mCheckStates == null && mSaveKeyringParcel == null) {
- return false;
- } else {
- return super.areAllItemsEnabled();
- }
- }
-
- // Disable selection of items for lists without checkboxes, http://stackoverflow.com/a/4075045
- @Override
- public boolean isEnabled(int position) {
- if (mCheckStates == null && mSaveKeyringParcel == null) {
- return false;
- } else {
- return super.isEnabled(position);
- }
- }
-
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/UserIdInfoDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/UserIdInfoDialogFragment.java
new file mode 100644
index 000000000..968b2429b
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/UserIdInfoDialogFragment.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui.dialog;
+
+import android.app.Activity;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.support.v4.app.DialogFragment;
+
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
+
+public class UserIdInfoDialogFragment extends DialogFragment {
+ private static final String ARG_IS_REVOKED = "is_revoked";
+ private static final String ARG_IS_VERIFIED = "is_verified";
+
+ /**
+ * Creates new instance of this dialog fragment
+ */
+ public static UserIdInfoDialogFragment newInstance(boolean isRevoked, int isVerified) {
+ UserIdInfoDialogFragment frag = new UserIdInfoDialogFragment();
+ Bundle args = new Bundle();
+ args.putBoolean(ARG_IS_REVOKED, isRevoked);
+ args.putInt(ARG_IS_VERIFIED, isVerified);
+
+ frag.setArguments(args);
+
+ return frag;
+ }
+
+ /**
+ * Creates dialog
+ */
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final Activity activity = getActivity();
+
+ int isVerified = getArguments().getInt(ARG_IS_VERIFIED);
+ boolean isRevoked = getArguments().getBoolean(ARG_IS_REVOKED);
+
+ CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(activity);
+
+ String title;
+ String message;
+ if (isRevoked) {
+ title = getString(R.string.user_id_info_revoked_title);
+ message = getString(R.string.user_id_info_revoked_text);
+ } else {
+ switch (isVerified) {
+ case KeychainContract.Certs.VERIFIED_SECRET:
+ title = getString(R.string.user_id_info_verified_title);
+ message = getString(R.string.user_id_info_verified_text);
+ break;
+ case KeychainContract.Certs.VERIFIED_SELF:
+ title = getString(R.string.user_id_info_not_verified_title);
+ message = getString(R.string.user_id_info_not_verified_text);
+ break;
+ default:
+ title = getString(R.string.user_id_info_invalid_title);
+ message = getString(R.string.user_id_info_invalid_text);
+ break;
+ }
+ }
+
+ alert.setTitle(title);
+ alert.setMessage(message);
+
+ alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int id) {
+ dismiss();
+ }
+ });
+
+ return alert.show();
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java
index 7e762fe77..20b9570bb 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java
@@ -22,6 +22,7 @@ import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Rect;
+import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.LoaderManager;
@@ -45,7 +46,7 @@ import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
-import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.util.Log;
import java.util.ArrayList;
@@ -113,9 +114,23 @@ public class EncryptKeyCompletionView extends TokenCompleteTextView {
((FragmentActivity) getContext()).getSupportLoaderManager().initLoader(0, null, new LoaderManager.LoaderCallbacks<Cursor>() {
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
- return new CursorLoader(getContext(), KeychainContract.KeyRings.buildUnifiedKeyRingsUri(),
- new String[]{KeychainContract.KeyRings.HAS_ENCRYPT, KeychainContract.KeyRings.KEY_ID, KeychainContract.KeyRings.USER_ID, KeychainContract.KeyRings.FINGERPRINT},
- null, null, null);
+ // These are the rows that we will retrieve.
+ Uri baseUri = KeyRings.buildUnifiedKeyRingsUri();
+
+ String[] projection = new String[]{
+ KeyRings._ID,
+ KeyRings.MASTER_KEY_ID,
+ KeyRings.KEY_ID,
+ KeyRings.USER_ID,
+ KeyRings.FINGERPRINT,
+ KeyRings.IS_EXPIRED,
+ KeyRings.HAS_ENCRYPT
+ };
+
+ String where = KeyRings.HAS_ENCRYPT + " NOT NULL AND " + KeyRings.IS_EXPIRED + " = 0 AND "
+ + KeyRings.IS_REVOKED + " = 0";
+
+ return new CursorLoader(getContext(), baseUri, projection, where, null, null);
}
@Override
@@ -148,10 +163,8 @@ public class EncryptKeyCompletionView extends TokenCompleteTextView {
ArrayList<EncryptionKey> keys = new ArrayList<EncryptionKey>();
while (cursor.moveToNext()) {
try {
- if (cursor.getInt(cursor.getColumnIndexOrThrow(KeychainContract.KeyRings.HAS_ENCRYPT)) != 0) {
- EncryptionKey key = new EncryptionKey(cursor);
- keys.add(key);
- }
+ EncryptionKey key = new EncryptionKey(cursor);
+ keys.add(key);
} catch (Exception e) {
Log.w(Constants.TAG, e);
return;
@@ -174,10 +187,10 @@ public class EncryptKeyCompletionView extends TokenCompleteTextView {
}
public EncryptionKey(Cursor cursor) {
- this(cursor.getString(cursor.getColumnIndexOrThrow(KeychainContract.KeyRings.USER_ID)),
- cursor.getLong(cursor.getColumnIndexOrThrow(KeychainContract.KeyRings.KEY_ID)),
+ this(cursor.getString(cursor.getColumnIndexOrThrow(KeyRings.USER_ID)),
+ cursor.getLong(cursor.getColumnIndexOrThrow(KeyRings.KEY_ID)),
PgpKeyHelper.convertFingerprintToHex(
- cursor.getBlob(cursor.getColumnIndexOrThrow(KeychainContract.KeyRings.FINGERPRINT))));
+ cursor.getBlob(cursor.getColumnIndexOrThrow(KeyRings.FINGERPRINT))));
}