aboutsummaryrefslogtreecommitdiffstats
path: root/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp
diff options
context:
space:
mode:
authorVincent Breitmoser <valodim@mugenguild.com>2014-03-12 23:45:21 +0100
committerVincent Breitmoser <valodim@mugenguild.com>2014-03-12 23:45:21 +0100
commita9c9b6132c7b122f8155ce9fc6c21c89e5b8c298 (patch)
tree78671569d3e84f5ed3db638a194981bc7c053a88 /OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp
parentedb98c67f4031b8c3c1d43b49bba733171119be2 (diff)
parent69f5bf6b577234053e700a43a4a7ba721e827c6a (diff)
downloadopen-keychain-a9c9b6132c7b122f8155ce9fc6c21c89e5b8c298.tar.gz
open-keychain-a9c9b6132c7b122f8155ce9fc6c21c89e5b8c298.tar.bz2
open-keychain-a9c9b6132c7b122f8155ce9fc6c21c89e5b8c298.zip
Merge branch 'master' into certs
Conflicts: OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
Diffstat (limited to 'OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp')
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConversionHelper.java6
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java206
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyResult.java88
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java3
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java80
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java138
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java12
7 files changed, 331 insertions, 202 deletions
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConversionHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConversionHelper.java
index ca97cbd9f..b268de3a6 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConversionHelper.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConversionHelper.java
@@ -78,7 +78,7 @@ public class PgpConversionHelper {
*
* Singles keys are encoded as keyRings with one single key in it by Bouncy Castle
*
- * @param keysBytes
+ * @param keyBytes
* @return
*/
public static PGPSecretKey BytesToPGPSecretKey(byte[] keyBytes) {
@@ -149,7 +149,7 @@ public class PgpConversionHelper {
/**
* Convert from PGPSecretKey to byte[]
*
- * @param keysBytes
+ * @param key
* @return
*/
public static byte[] PGPSecretKeyToBytes(PGPSecretKey key) {
@@ -165,7 +165,7 @@ public class PgpConversionHelper {
/**
* Convert from PGPSecretKeyRing to byte[]
*
- * @param keysBytes
+ * @param keyRing
* @return
*/
public static byte[] PGPSecretKeyRingToBytes(PGPSecretKeyRing keyRing) {
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
index fb97f3a5c..252be1036 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
@@ -18,8 +18,8 @@
package org.sufficientlysecure.keychain.pgp;
import android.content.Context;
-import android.os.Bundle;
+import org.openintents.openpgp.OpenPgpSignatureResult;
import org.spongycastle.bcpg.ArmoredInputStream;
import org.spongycastle.bcpg.SignatureSubpacketTags;
import org.spongycastle.openpgp.PGPCompressedData;
@@ -36,6 +36,7 @@ import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPPublicKeyEncryptedData;
import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.spongycastle.openpgp.PGPSecretKey;
+import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureList;
import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
@@ -53,7 +54,7 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.service.KeychainIntentService;
+import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ProgressDialogUpdater;
@@ -75,9 +76,10 @@ public class PgpDecryptVerify {
private InputData data;
private OutputStream outStream;
- private ProgressDialogUpdater progress;
- boolean assumeSymmetric;
- String passphrase;
+ private ProgressDialogUpdater progressDialogUpdater;
+ private boolean assumeSymmetric;
+ private String passphrase;
+ private long enforcedKeyId;
private PgpDecryptVerify(Builder builder) {
// private Constructor can only be called from Builder
@@ -85,9 +87,10 @@ public class PgpDecryptVerify {
this.data = builder.data;
this.outStream = builder.outStream;
- this.progress = builder.progress;
+ this.progressDialogUpdater = builder.progressDialogUpdater;
this.assumeSymmetric = builder.assumeSymmetric;
this.passphrase = builder.passphrase;
+ this.enforcedKeyId = builder.enforcedKeyId;
}
public static class Builder {
@@ -97,9 +100,10 @@ public class PgpDecryptVerify {
private OutputStream outStream;
// optional
- private ProgressDialogUpdater progress = null;
+ private ProgressDialogUpdater progressDialogUpdater = null;
private boolean assumeSymmetric = false;
private String passphrase = "";
+ private long enforcedKeyId = 0;
public Builder(Context context, InputData data, OutputStream outStream) {
this.context = context;
@@ -107,8 +111,8 @@ public class PgpDecryptVerify {
this.outStream = outStream;
}
- public Builder progress(ProgressDialogUpdater progress) {
- this.progress = progress;
+ public Builder progressDialogUpdater(ProgressDialogUpdater progressDialogUpdater) {
+ this.progressDialogUpdater = progressDialogUpdater;
return this;
}
@@ -122,20 +126,32 @@ public class PgpDecryptVerify {
return this;
}
+ /**
+ * Allow this key id alone for decryption.
+ * This means only ciphertexts encrypted for this private key can be decrypted.
+ *
+ * @param enforcedKeyId
+ * @return
+ */
+ public Builder enforcedKeyId(long enforcedKeyId) {
+ this.enforcedKeyId = enforcedKeyId;
+ return this;
+ }
+
public PgpDecryptVerify build() {
return new PgpDecryptVerify(this);
}
}
public void updateProgress(int message, int current, int total) {
- if (progress != null) {
- progress.setProgress(message, current, total);
+ if (progressDialogUpdater != null) {
+ progressDialogUpdater.setProgress(message, current, total);
}
}
public void updateProgress(int current, int total) {
- if (progress != null) {
- progress.setProgress(current, total);
+ if (progressDialogUpdater != null) {
+ progressDialogUpdater.setProgress(current, total);
}
}
@@ -177,9 +193,8 @@ public class PgpDecryptVerify {
* @throws PGPException
* @throws SignatureException
*/
- public Bundle execute()
+ public PgpDecryptVerifyResult execute()
throws IOException, PgpGeneralException, PGPException, SignatureException {
-
// automatically works with ascii armor input and binary
InputStream in = PGPUtil.getDecoderStream(data.getInputStream());
if (in instanceof ArmoredInputStream) {
@@ -207,9 +222,9 @@ public class PgpDecryptVerify {
* @throws PGPException
* @throws SignatureException
*/
- private Bundle decryptVerify(InputStream in)
+ private PgpDecryptVerifyResult decryptVerify(InputStream in)
throws IOException, PgpGeneralException, PGPException, SignatureException {
- Bundle returnData = new Bundle();
+ PgpDecryptVerifyResult returnData = new PgpDecryptVerifyResult();
PGPObjectFactory pgpF = new PGPObjectFactory(in);
PGPEncryptedDataList enc;
@@ -277,9 +292,40 @@ public class PgpDecryptVerify {
PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) obj;
secretKey = ProviderHelper.getPGPSecretKeyByKeyId(context, encData.getKeyID());
if (secretKey != null) {
+ // secret key exists in database
+
+ // allow only a specific key for decryption?
+ if (enforcedKeyId != 0) {
+ // TODO: improve this code! get master key directly!
+ PGPSecretKeyRing secretKeyRing = ProviderHelper.getPGPSecretKeyRingByKeyId(context, encData.getKeyID());
+ long masterKeyId = PgpKeyHelper.getMasterKey(secretKeyRing).getKeyID();
+ Log.d(Constants.TAG, "encData.getKeyID():" + encData.getKeyID());
+ Log.d(Constants.TAG, "enforcedKeyId: " + enforcedKeyId);
+ Log.d(Constants.TAG, "masterKeyId: " + masterKeyId);
+
+ if (enforcedKeyId != masterKeyId) {
+ throw new PgpGeneralException(context.getString(R.string.error_no_secret_key_found));
+ }
+ }
+
pbe = encData;
+
+ // if no passphrase was explicitly set try to get it from the cache service
+ if (passphrase == null) {
+ // returns "" if key has no passphrase
+ passphrase = PassphraseCacheService.getCachedPassphrase(context, encData.getKeyID());
+
+ // if passphrase was not cached, return here indicating that a passphrase is missing!
+ if (passphrase == null) {
+ returnData.setKeyPassphraseNeeded(true);
+ return returnData;
+ }
+ }
+
break;
}
+
+
}
}
@@ -289,7 +335,7 @@ public class PgpDecryptVerify {
currentProgress += 5;
updateProgress(R.string.progress_extracting_key, currentProgress, 100);
- PGPPrivateKey privateKey = null;
+ PGPPrivateKey privateKey;
try {
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
@@ -317,6 +363,7 @@ public class PgpDecryptVerify {
PGPObjectFactory plainFact = new PGPObjectFactory(clear);
Object dataChunk = plainFact.nextObject();
PGPOnePassSignature signature = null;
+ OpenPgpSignatureResult signatureResult = null;
PGPPublicKey signatureKey = null;
int signatureIndex = -1;
@@ -334,7 +381,7 @@ public class PgpDecryptVerify {
if (dataChunk instanceof PGPOnePassSignatureList) {
updateProgress(R.string.progress_processing_signature, currentProgress, 100);
- returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE, true);
+ signatureResult = new OpenPgpSignatureResult();
PGPOnePassSignatureList sigList = (PGPOnePassSignatureList) dataChunk;
for (int i = 0; i < sigList.size(); ++i) {
signature = sigList.get(i);
@@ -354,12 +401,12 @@ public class PgpDecryptVerify {
if (signKeyRing != null) {
userId = PgpKeyHelper.getMainUserId(PgpKeyHelper.getMasterKey(signKeyRing));
}
- returnData.putString(KeychainIntentService.RESULT_SIGNATURE_USER_ID, userId);
+ signatureResult.setUserId(userId);
break;
}
}
- returnData.putLong(KeychainIntentService.RESULT_SIGNATURE_KEY_ID, signatureKeyId);
+ signatureResult.setKeyId(signatureKeyId);
if (signature != null) {
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = new JcaPGPContentVerifierBuilderProvider()
@@ -367,7 +414,7 @@ public class PgpDecryptVerify {
signature.init(contentVerifierBuilderProvider, signatureKey);
} else {
- returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_UNKNOWN, true);
+ signatureResult.setStatus(OpenPgpSignatureResult.SIGNATURE_UNKNOWN_PUB_KEY);
}
dataChunk = plainFact.nextObject();
@@ -405,8 +452,7 @@ public class PgpDecryptVerify {
try {
signature.update(buffer, 0, n);
} catch (SignatureException e) {
- returnData
- .putBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS, false);
+ signatureResult.setStatus(OpenPgpSignatureResult.SIGNATURE_ERROR);
signature = null;
}
}
@@ -430,17 +476,20 @@ public class PgpDecryptVerify {
PGPSignature messageSignature = signatureList.get(signatureIndex);
// these are not cleartext signatures!
- returnData.putBoolean(KeychainIntentService.RESULT_CLEARTEXT_SIGNATURE_ONLY, false);
+ // TODO: what about binary signatures?
+ signatureResult.setSignatureOnly(false);
//Now check binding signatures
- boolean keyBinding_isok = verifyKeyBinding(context, messageSignature, signatureKey);
- boolean sig_isok = signature.verify(messageSignature);
+ boolean validKeyBinding = verifyKeyBinding(context, messageSignature, signatureKey);
+ boolean validSignature = signature.verify(messageSignature);
- returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS, keyBinding_isok & sig_isok);
+ // TODO: implement CERTIFIED!
+ if (validKeyBinding & validSignature) {
+ signatureResult.setStatus(OpenPgpSignatureResult.SIGNATURE_SUCCESS_UNCERTIFIED);
+ }
}
}
- // TODO: test if this integrity really check works!
if (encryptedData.isIntegrityProtected()) {
updateProgress(R.string.progress_verifying_integrity, 95, 100);
@@ -455,9 +504,12 @@ public class PgpDecryptVerify {
} else {
// no integrity check
Log.e(Constants.TAG, "Encrypted data was not integrity protected!");
+ // TODO: inform user?
}
updateProgress(R.string.progress_done, 100, 100);
+
+ returnData.setSignatureResult(signatureResult);
return returnData;
}
@@ -474,11 +526,12 @@ public class PgpDecryptVerify {
* @throws PGPException
* @throws SignatureException
*/
- private Bundle verifyCleartextSignature(ArmoredInputStream aIn)
+ private PgpDecryptVerifyResult verifyCleartextSignature(ArmoredInputStream aIn)
throws IOException, PgpGeneralException, PGPException, SignatureException {
- Bundle returnData = new Bundle();
+ PgpDecryptVerifyResult returnData = new PgpDecryptVerifyResult();
+ OpenPgpSignatureResult signatureResult = new OpenPgpSignatureResult();
// cleartext signatures are never encrypted ;)
- returnData.putBoolean(KeychainIntentService.RESULT_CLEARTEXT_SIGNATURE_ONLY, true);
+ signatureResult.setSignatureOnly(true);
ByteArrayOutputStream out = new ByteArrayOutputStream();
@@ -504,8 +557,6 @@ public class PgpDecryptVerify {
byte[] clearText = out.toByteArray();
outStream.write(clearText);
- returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE, true);
-
updateProgress(R.string.progress_processing_signature, 60, 100);
PGPObjectFactory pgpFact = new PGPObjectFactory(aIn);
@@ -533,15 +584,17 @@ public class PgpDecryptVerify {
if (signKeyRing != null) {
userId = PgpKeyHelper.getMainUserId(PgpKeyHelper.getMasterKey(signKeyRing));
}
- returnData.putString(KeychainIntentService.RESULT_SIGNATURE_USER_ID, userId);
+ signatureResult.setUserId(userId);
break;
}
}
- returnData.putLong(KeychainIntentService.RESULT_SIGNATURE_KEY_ID, signatureKeyId);
+ signatureResult.setKeyId(signatureKeyId);
if (signature == null) {
- returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_UNKNOWN, true);
+ signatureResult.setStatus(OpenPgpSignatureResult.SIGNATURE_UNKNOWN_PUB_KEY);
+ returnData.setSignatureResult(signatureResult);
+
updateProgress(R.string.progress_done, 100, 100);
return returnData;
}
@@ -569,12 +622,17 @@ public class PgpDecryptVerify {
} while (lookAhead != -1);
}
- boolean sig_isok = signature.verify();
-
//Now check binding signatures
- boolean keyBinding_isok = verifyKeyBinding(context, signature, signatureKey);
+ boolean validKeyBinding = verifyKeyBinding(context, signature, signatureKey);
+ boolean validSignature = signature.verify();
- returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS, sig_isok & keyBinding_isok);
+ if (validSignature & validKeyBinding) {
+ signatureResult.setStatus(OpenPgpSignatureResult.SIGNATURE_SUCCESS_UNCERTIFIED);
+ }
+
+ // TODO: what about SIGNATURE_SUCCESS_CERTIFIED and SIGNATURE_ERROR????
+
+ returnData.setSignatureResult(signatureResult);
updateProgress(R.string.progress_done, 100, 100);
return returnData;
@@ -582,34 +640,34 @@ public class PgpDecryptVerify {
private static boolean verifyKeyBinding(Context context, PGPSignature signature, PGPPublicKey signatureKey) {
long signatureKeyId = signature.getKeyID();
- boolean keyBinding_isok = false;
- String userId = null;
+ boolean validKeyBinding = false;
+
PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingByKeyId(context,
signatureKeyId);
PGPPublicKey mKey = null;
if (signKeyRing != null) {
mKey = PgpKeyHelper.getMasterKey(signKeyRing);
}
+
if (signature.getKeyID() != mKey.getKeyID()) {
- keyBinding_isok = verifyKeyBinding(mKey, signatureKey);
+ validKeyBinding = verifyKeyBinding(mKey, signatureKey);
} else { //if the key used to make the signature was the master key, no need to check binding sigs
- keyBinding_isok = true;
+ validKeyBinding = true;
}
- return keyBinding_isok;
+ return validKeyBinding;
}
private static boolean verifyKeyBinding(PGPPublicKey masterPublicKey, PGPPublicKey signingPublicKey) {
- boolean subkeyBinding_isok = false;
- boolean tmp_subkeyBinding_isok = false;
- boolean primkeyBinding_isok = false;
- JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = new JcaPGPContentVerifierBuilderProvider()
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ boolean validSubkeyBinding = false;
+ boolean validTempSubkeyBinding = false;
+ boolean validPrimaryKeyBinding = false;
+
+ JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
+ new JcaPGPContentVerifierBuilderProvider()
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
Iterator<PGPSignature> itr = signingPublicKey.getSignatures();
- subkeyBinding_isok = false;
- tmp_subkeyBinding_isok = false;
- primkeyBinding_isok = false;
while (itr.hasNext()) { //what does gpg do if the subkey binding is wrong?
//gpg has an invalid subkey binding error on key import I think, but doesn't shout
//about keys without subkey signing. Can't get it to import a slightly broken one
@@ -619,32 +677,36 @@ public class PgpDecryptVerify {
//check and if ok, check primary key binding.
try {
sig.init(contentVerifierBuilderProvider, masterPublicKey);
- tmp_subkeyBinding_isok = sig.verifyCertification(masterPublicKey, signingPublicKey);
+ validTempSubkeyBinding = sig.verifyCertification(masterPublicKey, signingPublicKey);
} catch (PGPException e) {
continue;
} catch (SignatureException e) {
continue;
}
- if (tmp_subkeyBinding_isok)
- subkeyBinding_isok = true;
- if (tmp_subkeyBinding_isok) {
- primkeyBinding_isok = verifyPrimaryBinding(sig.getUnhashedSubPackets(), masterPublicKey, signingPublicKey);
- if (primkeyBinding_isok)
+ if (validTempSubkeyBinding)
+ validSubkeyBinding = true;
+ if (validTempSubkeyBinding) {
+ validPrimaryKeyBinding = verifyPrimaryKeyBinding(sig.getUnhashedSubPackets(),
+ masterPublicKey, signingPublicKey);
+ if (validPrimaryKeyBinding)
break;
- primkeyBinding_isok = verifyPrimaryBinding(sig.getHashedSubPackets(), masterPublicKey, signingPublicKey);
- if (primkeyBinding_isok)
+ validPrimaryKeyBinding = verifyPrimaryKeyBinding(sig.getHashedSubPackets(),
+ masterPublicKey, signingPublicKey);
+ if (validPrimaryKeyBinding)
break;
}
}
}
- return (subkeyBinding_isok & primkeyBinding_isok);
+ return (validSubkeyBinding & validPrimaryKeyBinding);
}
- private static boolean verifyPrimaryBinding(PGPSignatureSubpacketVector Pkts, PGPPublicKey masterPublicKey, PGPPublicKey signingPublicKey) {
- boolean primkeyBinding_isok = false;
- JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = new JcaPGPContentVerifierBuilderProvider()
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ private static boolean verifyPrimaryKeyBinding(PGPSignatureSubpacketVector Pkts,
+ PGPPublicKey masterPublicKey, PGPPublicKey signingPublicKey) {
+ boolean validPrimaryKeyBinding = false;
+ JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
+ new JcaPGPContentVerifierBuilderProvider()
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
PGPSignatureList eSigList;
if (Pkts.hasSubpacket(SignatureSubpacketTags.EMBEDDED_SIGNATURE)) {
@@ -660,8 +722,8 @@ public class PgpDecryptVerify {
if (emSig.getSignatureType() == PGPSignature.PRIMARYKEY_BINDING) {
try {
emSig.init(contentVerifierBuilderProvider, signingPublicKey);
- primkeyBinding_isok = emSig.verifyCertification(masterPublicKey, signingPublicKey);
- if (primkeyBinding_isok)
+ validPrimaryKeyBinding = emSig.verifyCertification(masterPublicKey, signingPublicKey);
+ if (validPrimaryKeyBinding)
break;
} catch (PGPException e) {
continue;
@@ -671,7 +733,8 @@ public class PgpDecryptVerify {
}
}
}
- return primkeyBinding_isok;
+
+ return validPrimaryKeyBinding;
}
/**
@@ -680,10 +743,9 @@ public class PgpDecryptVerify {
* @param sig
* @param line
* @throws SignatureException
- * @throws IOException
*/
private static void processLine(PGPSignature sig, byte[] line)
- throws SignatureException, IOException {
+ throws SignatureException {
int length = getLengthWithoutWhiteSpace(line);
if (length > 0) {
sig.update(line, 0, length);
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyResult.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyResult.java
new file mode 100644
index 000000000..0477c4fdf
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyResult.java
@@ -0,0 +1,88 @@
+/*
+ * 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.pgp;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import org.openintents.openpgp.OpenPgpSignatureResult;
+
+public class PgpDecryptVerifyResult implements Parcelable {
+ boolean symmetricPassphraseNeeded;
+ boolean keyPassphraseNeeded;
+ OpenPgpSignatureResult signatureResult;
+
+ public boolean isSymmetricPassphraseNeeded() {
+ return symmetricPassphraseNeeded;
+ }
+
+ public void setSymmetricPassphraseNeeded(boolean symmetricPassphraseNeeded) {
+ this.symmetricPassphraseNeeded = symmetricPassphraseNeeded;
+ }
+
+ public boolean isKeyPassphraseNeeded() {
+ return keyPassphraseNeeded;
+ }
+
+ public void setKeyPassphraseNeeded(boolean keyPassphraseNeeded) {
+ this.keyPassphraseNeeded = keyPassphraseNeeded;
+ }
+
+ public OpenPgpSignatureResult getSignatureResult() {
+ return signatureResult;
+ }
+
+ public void setSignatureResult(OpenPgpSignatureResult signatureResult) {
+ this.signatureResult = signatureResult;
+ }
+
+ public PgpDecryptVerifyResult() {
+
+ }
+
+ public PgpDecryptVerifyResult(PgpDecryptVerifyResult b) {
+ this.symmetricPassphraseNeeded = b.symmetricPassphraseNeeded;
+ this.keyPassphraseNeeded = b.keyPassphraseNeeded;
+ this.signatureResult = b.signatureResult;
+ }
+
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeByte((byte) (symmetricPassphraseNeeded ? 1 : 0));
+ dest.writeByte((byte) (keyPassphraseNeeded ? 1 : 0));
+ dest.writeParcelable(signatureResult, 0);
+ }
+
+ public static final Creator<PgpDecryptVerifyResult> CREATOR = new Creator<PgpDecryptVerifyResult>() {
+ public PgpDecryptVerifyResult createFromParcel(final Parcel source) {
+ PgpDecryptVerifyResult vr = new PgpDecryptVerifyResult();
+ vr.symmetricPassphraseNeeded = source.readByte() == 1;
+ vr.keyPassphraseNeeded = source.readByte() == 1;
+ vr.signatureResult = source.readParcelable(OpenPgpSignatureResult.class.getClassLoader());
+ return vr;
+ }
+
+ public PgpDecryptVerifyResult[] newArray(final int size) {
+ return new PgpDecryptVerifyResult[size];
+ }
+ };
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java
index 7ac904d89..1db4f98b1 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java
@@ -193,11 +193,10 @@ public class PgpHelper {
* @param context
* @param progress
* @param file
- * @throws FileNotFoundException
* @throws IOException
*/
public static void deleteFileSecurely(Context context, ProgressDialogUpdater progress, File file)
- throws FileNotFoundException, IOException {
+ throws IOException {
long length = file.length();
SecureRandom random = new SecureRandom();
RandomAccessFile raf = new RandomAccessFile(file, "rws");
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java
index a7a574ee7..138e54f71 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java
@@ -17,11 +17,9 @@
package org.sufficientlysecure.keychain.pgp;
-import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
-import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
@@ -29,12 +27,10 @@ import java.util.List;
import org.spongycastle.bcpg.ArmoredOutputStream;
import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPKeyRing;
-import org.spongycastle.openpgp.PGPObjectFactory;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
-import org.spongycastle.openpgp.PGPUtil;
import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
@@ -44,11 +40,9 @@ import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry;
import org.sufficientlysecure.keychain.util.HkpKeyServer;
-import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.KeyServer.AddKeyException;
import org.sufficientlysecure.keychain.util.Log;
-import org.sufficientlysecure.keychain.util.PositionAwareInputStream;
import org.sufficientlysecure.keychain.util.ProgressDialogUpdater;
import android.content.Context;
@@ -85,13 +79,14 @@ public class PgpImportExport {
public boolean uploadKeyRingToServer(HkpKeyServer server, PGPPublicKeyRing keyring) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
- ArmoredOutputStream aos = new ArmoredOutputStream(bos);
+ ArmoredOutputStream aos = null;
try {
+ aos = new ArmoredOutputStream(bos);
aos.write(keyring.getEncoded());
aos.close();
- String armouredKey = bos.toString("UTF-8");
- server.add(armouredKey);
+ String armoredKey = bos.toString("UTF-8");
+ server.add(armoredKey);
return true;
} catch (IOException e) {
@@ -101,7 +96,8 @@ public class PgpImportExport {
return false;
} finally {
try {
- bos.close();
+ if (aos != null) aos.close();
+ if (bos != null) bos.close();
} catch (IOException e) {
}
}
@@ -161,59 +157,53 @@ public class PgpImportExport {
return returnData;
}
- public Bundle exportKeyRings(ArrayList<Long> keyRingMasterKeyIds, int keyType,
- OutputStream outStream) throws PgpGeneralException, FileNotFoundException,
+ public Bundle exportKeyRings(ArrayList<Long> keyRingRowIds, int keyType,
+ OutputStream outStream) throws PgpGeneralException,
PGPException, IOException {
Bundle returnData = new Bundle();
+ int rowIdsSize = keyRingRowIds.size();
+
updateProgress(
mContext.getResources().getQuantityString(R.plurals.progress_exporting_key,
- keyRingMasterKeyIds.size()), 0, 100);
+ rowIdsSize), 0, 100);
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
throw new PgpGeneralException(
mContext.getString(R.string.error_external_storage_not_ready));
}
-
- if (keyType == Id.type.secret_key) {
- ArmoredOutputStream outSec = new ArmoredOutputStream(outStream);
- outSec.setHeader("Version", PgpHelper.getFullVersion(mContext));
-
- for (int i = 0; i < keyRingMasterKeyIds.size(); ++i) {
- updateProgress(i * 100 / keyRingMasterKeyIds.size() / 2, 100);
-
- PGPSecretKeyRing secretKeyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(
- mContext, keyRingMasterKeyIds.get(i));
+ // For each row id
+ for (int i = 0; i < rowIdsSize; ++i) {
+ // Create an output stream
+ ArmoredOutputStream arOutStream = new ArmoredOutputStream(outStream);
+ arOutStream.setHeader("Version", PgpHelper.getFullVersion(mContext));
+
+ // If the keyType is secret get the PGPSecretKeyRing
+ // based on the row id and encode it to the output
+ if (keyType == Id.type.secret_key) {
+ updateProgress(i * 100 / rowIdsSize / 2, 100);
+ PGPSecretKeyRing secretKeyRing =
+ ProviderHelper.getPGPSecretKeyRingByRowId(mContext, keyRingRowIds.get(i));
if (secretKeyRing != null) {
- secretKeyRing.encode(outSec);
- }
- }
- outSec.close();
- } else {
- // export public keyrings...
- ArmoredOutputStream outPub = new ArmoredOutputStream(outStream);
- outPub.setHeader("Version", PgpHelper.getFullVersion(mContext));
-
- for (int i = 0; i < keyRingMasterKeyIds.size(); ++i) {
- // double the needed time if exporting both public and secret parts
- if (keyType == Id.type.secret_key) {
- updateProgress(i * 100 / keyRingMasterKeyIds.size() / 2, 100);
- } else {
- updateProgress(i * 100 / keyRingMasterKeyIds.size(), 100);
+ secretKeyRing.encode(arOutStream);
}
-
- PGPPublicKeyRing publicKeyRing = ProviderHelper.getPGPPublicKeyRingByMasterKeyId(
- mContext, keyRingMasterKeyIds.get(i));
+ // Else if it's a public key get the PGPPublicKeyRing
+ // and encode that to the output
+ } else {
+ updateProgress(i * 100 / rowIdsSize, 100);
+ PGPPublicKeyRing publicKeyRing =
+ ProviderHelper.getPGPPublicKeyRingByRowId(mContext, keyRingRowIds.get(i));
if (publicKeyRing != null) {
- publicKeyRing.encode(outPub);
+ publicKeyRing.encode(arOutStream);
}
}
- outPub.close();
+
+ arOutStream.close();
}
- returnData.putInt(KeychainIntentService.RESULT_EXPORT, keyRingMasterKeyIds.size());
+ returnData.putInt(KeychainIntentService.RESULT_EXPORT, rowIdsSize);
updateProgress(R.string.progress_done, 100, 100);
@@ -234,7 +224,7 @@ public class PgpImportExport {
for (PGPSecretKey testSecretKey : new IterableIterator<PGPSecretKey>(
secretKeyRing.getSecretKeys())) {
if (!testSecretKey.isMasterKey()) {
- if (PgpKeyHelper.isSecretKeyPrivateEmpty(testSecretKey)) {
+ if (testSecretKey.isPrivateKeyEmpty()) {
// this is bad, something is very wrong...
save = false;
status = Id.return_value.bad;
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java
index 38480a766..902dd8da9 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java
@@ -32,6 +32,7 @@ import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
+import org.spongycastle.util.encoders.Hex;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
@@ -415,28 +416,27 @@ public class PgpKeyHelper {
String algorithmStr = null;
switch (algorithm) {
- case PGPPublicKey.RSA_ENCRYPT:
- case PGPPublicKey.RSA_GENERAL:
- case PGPPublicKey.RSA_SIGN: {
- algorithmStr = "RSA";
- break;
- }
-
- case PGPPublicKey.DSA: {
- algorithmStr = "DSA";
- break;
- }
+ case PGPPublicKey.RSA_ENCRYPT:
+ case PGPPublicKey.RSA_GENERAL:
+ case PGPPublicKey.RSA_SIGN: {
+ algorithmStr = "RSA";
+ break;
+ }
+ case PGPPublicKey.DSA: {
+ algorithmStr = "DSA";
+ break;
+ }
- case PGPPublicKey.ELGAMAL_ENCRYPT:
- case PGPPublicKey.ELGAMAL_GENERAL: {
- algorithmStr = "ElGamal";
- break;
- }
+ case PGPPublicKey.ELGAMAL_ENCRYPT:
+ case PGPPublicKey.ELGAMAL_GENERAL: {
+ algorithmStr = "ElGamal";
+ break;
+ }
- default: {
- algorithmStr = "Unknown";
- break;
- }
+ default: {
+ algorithmStr = "Unknown";
+ break;
+ }
}
if(keySize > 0)
return algorithmStr + ", " + keySize + " bit";
@@ -444,31 +444,6 @@ public class PgpKeyHelper {
return algorithmStr;
}
- /**
- * Converts fingerprint to hex with whitespaces after 4 characters
- *
- * @param fp
- * @return
- */
- public static String convertFingerprintToHex(byte[] fp, boolean chunked) {
- String fingerPrint = "";
- for (int i = 0; i < fp.length; ++i) {
- if (chunked && i != 0 && i % 10 == 0) {
- fingerPrint += " ";
- } else if (chunked && i != 0 && i % 2 == 0) {
- fingerPrint += " ";
- }
- String chunk = Integer.toHexString((fp[i] + 256) % 256).toUpperCase(Locale.US);
- while (chunk.length() < 2) {
- chunk = "0" + chunk;
- }
- fingerPrint += chunk;
- }
-
- return fingerPrint;
-
- }
-
public static String getFingerPrint(Context context, long keyId) {
PGPPublicKey key = ProviderHelper.getPGPPublicKeyByKeyId(context, keyId);
// if it is no public key get it from your own keys...
@@ -484,52 +459,68 @@ public class PgpKeyHelper {
return convertFingerprintToHex(key.getFingerprint(), true);
}
- public static boolean isSecretKeyPrivateEmpty(PGPSecretKey secretKey) {
- return secretKey.isPrivateKeyEmpty();
- }
-
-// public static boolean isSecretKeyPrivateEmpty(Context context, long keyId) {
-// PGPSecretKey secretKey = ProviderHelper.getPGPSecretKeyByKeyId(context, keyId);
-// if (secretKey == null) {
-// Log.e(Constants.TAG, "Key could not be found!");
-// return false; // could be a public key, assume it is not empty
-// }
-// return isSecretKeyPrivateEmpty(secretKey);
-// }
-
- public static String convertKeyIdToHex(long keyId) {
- String fingerPrint = Long.toHexString(keyId & 0xffffffffL).toUpperCase(Locale.US);
- while (fingerPrint.length() < 8) {
- fingerPrint = "0" + fingerPrint;
+ /**
+ * Converts fingerprint to hex (optional: with whitespaces after 4 characters)
+ * <p/>
+ * Fingerprint is shown using lowercase characters. Studies have shown that humans can
+ * better differentiate between numbers and letters when letters are lowercase.
+ *
+ * @param fingerprint
+ * @param split split into 4 character chunks
+ * @return
+ */
+ public static String convertFingerprintToHex(byte[] fingerprint, boolean split) {
+ String hexString = Hex.toHexString(fingerprint);
+ if (split) {
+ hexString = hexString.replaceAll("(.{4})(?!$)", "$1 ");
}
- return fingerPrint;
+
+ return hexString;
}
/**
- * TODO: documentation
- *
+ * Convert key id from long to 64 bit hex string
+ * <p/>
+ * V4: "The Key ID is the low-order 64 bits of the fingerprint"
+ * <p/>
+ * see http://tools.ietf.org/html/rfc4880#section-12.2
+ *
* @param keyId
* @return
*/
- public static String convertKeyToHex(long keyId) {
- return convertKeyIdToHex(keyId >> 32) + convertKeyIdToHex(keyId);
+ public static String convertKeyIdToHex(long keyId) {
+ return "0x" + convertKeyIdToHex32bit(keyId >> 32) + convertKeyIdToHex32bit(keyId);
+ }
+
+ private static String convertKeyIdToHex32bit(long keyId) {
+ String hexString = Long.toHexString(keyId & 0xffffffffL).toLowerCase(Locale.US);
+ while (hexString.length() < 8) {
+ hexString = "0" + hexString;
+ }
+ return hexString;
}
- public static long convertHexToKeyId(String data) {
- int len = data.length();
- String s2 = data.substring(len - 8);
- String s1 = data.substring(0, len - 8);
+ /**
+ * Used in HkpKeyServer to convert hex encoded key ids back to long.
+ *
+ * @param hexString
+ * @return
+ */
+ public static long convertHexToKeyId(String hexString) {
+ int len = hexString.length();
+ String s2 = hexString.substring(len - 8);
+ String s1 = hexString.substring(0, len - 8);
return (Long.parseLong(s1, 16) << 32) | Long.parseLong(s2, 16);
}
/**
* Splits userId string into naming part, email part, and comment part
- *
+ *
* @param userId
* @return array with naming (0), email (1), comment (2)
*/
public static String[] splitUserId(String userId) {
- String[] result = new String[] { null, null, null };
+ String[] result = new String[]{null, null, null};
if (userId == null || userId.equals("")) {
return result;
@@ -550,7 +541,6 @@ public class PgpKeyHelper {
result[0] = matcher.group(1);
result[1] = matcher.group(3);
result[2] = matcher.group(2);
- return result;
}
return result;
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
index 7caee4048..5ebb53f20 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
@@ -107,7 +107,7 @@ public class PgpKeyOperation {
*
* @param algorithmChoice
* @param keySize
- * @param passPhrase
+ * @param passphrase
* @param isMasterKey
* @return
* @throws NoSuchAlgorithmException
@@ -118,7 +118,7 @@ public class PgpKeyOperation {
*/
// TODO: key flags?
- public PGPSecretKey createKey(int algorithmChoice, int keySize, String passPhrase,
+ public PGPSecretKey createKey(int algorithmChoice, int keySize, String passphrase,
boolean isMasterKey) throws NoSuchAlgorithmException, PGPException, NoSuchProviderException,
PgpGeneralException, InvalidAlgorithmParameterException {
@@ -126,8 +126,8 @@ public class PgpKeyOperation {
throw new PgpGeneralException(mContext.getString(R.string.error_key_size_minimum512bit));
}
- if (passPhrase == null) {
- passPhrase = "";
+ if (passphrase == null) {
+ passphrase = "";
}
int algorithm = 0;
@@ -181,7 +181,7 @@ public class PgpKeyOperation {
// Build key encrypter and decrypter based on passphrase
PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder(
PGPEncryptedData.CAST5, sha1Calc)
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passPhrase.toCharArray());
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
PGPSecretKey secKey = new PGPSecretKey(keyPair.getPrivateKey(), keyPair.getPublicKey(),
sha1Calc, isMasterKey, keyEncryptor);
@@ -190,7 +190,7 @@ public class PgpKeyOperation {
}
public void changeSecretKeyPassphrase(PGPSecretKeyRing keyRing, String oldPassPhrase,
- String newPassPhrase) throws IOException, PGPException, PGPException,
+ String newPassPhrase) throws IOException, PGPException,
NoSuchProviderException {
updateProgress(R.string.progress_building_key, 0, 100);