aboutsummaryrefslogtreecommitdiffstats
path: root/OpenPGP-Keychain
diff options
context:
space:
mode:
authorDominik Schürmann <dominik@dominikschuermann.de>2014-04-01 16:15:55 +0200
committerDominik Schürmann <dominik@dominikschuermann.de>2014-04-01 16:15:55 +0200
commit53605855ee747d4c20578a322629152b47bd08fe (patch)
tree75ecb570f16e746f3741ba3ead23ae32ce5d8d0d /OpenPGP-Keychain
parent7000ab37fda358b93ec89dd77b5b2b42c5ec6420 (diff)
parent247ad6207ae5489d6dfac73079ce5933415409a0 (diff)
downloadopen-keychain-53605855ee747d4c20578a322629152b47bd08fe.tar.gz
open-keychain-53605855ee747d4c20578a322629152b47bd08fe.tar.bz2
open-keychain-53605855ee747d4c20578a322629152b47bd08fe.zip
Merge branch 'master' of github.com:openpgp-keychain/openpgp-keychain
Diffstat (limited to 'OpenPGP-Keychain')
-rw-r--r--OpenPGP-Keychain/build.gradle42
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java216
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralException.java3
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralMsgIdException.java35
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java47
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/ProgressScaler.java50
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/key_server_preference.xml2
-rw-r--r--OpenPGP-Keychain/src/test/java/org/sufficientlysecure/keychain/PgpKeyOperationTest.java46
-rw-r--r--OpenPGP-Keychain/src/test/java/org/sufficientlysecure/keychain/RobolectricGradleTestRunner.java23
9 files changed, 347 insertions, 117 deletions
diff --git a/OpenPGP-Keychain/build.gradle b/OpenPGP-Keychain/build.gradle
index 04ee46715..7fd1fecce 100644
--- a/OpenPGP-Keychain/build.gradle
+++ b/OpenPGP-Keychain/build.gradle
@@ -1,5 +1,12 @@
apply plugin: 'android'
+sourceSets {
+ testLocal {
+ java.srcDir file('src/test/java')
+ resources.srcDir file('src/test/resources')
+ }
+}
+
dependencies {
compile 'com.android.support:support-v4:19.0.1'
compile 'com.android.support:appcompat-v7:19.0.1'
@@ -15,6 +22,25 @@ dependencies {
compile project(':libraries:spongycastle:pkix')
compile project(':libraries:spongycastle:prov')
compile project(':libraries:Android-AppMsg:library')
+
+ // Dependencies for the `testLocal` task, make sure to list all your global dependencies here as well
+ testLocalCompile 'junit:junit:4.11'
+ testLocalCompile 'org.robolectric:robolectric:2.1.+'
+ testLocalCompile 'com.google.android:android:4.1.1.4'
+ testLocalCompile 'com.android.support:support-v4:19.0.1'
+ testLocalCompile 'com.android.support:appcompat-v7:19.0.1'
+ testLocalCompile project(':OpenPGP-Keychain-API:libraries:openpgp-api-library')
+ testLocalCompile project(':OpenPGP-Keychain-API:libraries:openkeychain-api-library')
+ testLocalCompile project(':libraries:HtmlTextView')
+ testLocalCompile project(':libraries:StickyListHeaders:library')
+ testLocalCompile project(':libraries:AndroidBootstrap')
+ testLocalCompile project(':libraries:zxing')
+ testLocalCompile project(':libraries:zxing-android-integration')
+ testLocalCompile project(':libraries:spongycastle:core')
+ testLocalCompile project(':libraries:spongycastle:pg')
+ testLocalCompile project(':libraries:spongycastle:pkix')
+ testLocalCompile project(':libraries:spongycastle:prov')
+ testLocalCompile project(':libraries:Android-AppMsg:library')
}
android {
@@ -61,3 +87,19 @@ android {
htmlOutput file("lint-report.html")
}
}
+
+task localTest(type: Test, dependsOn: assemble) {
+ testClassesDir = sourceSets.testLocal.output.classesDir
+
+ android.sourceSets.main.java.srcDirs.each { dir ->
+ def buildDir = dir.getAbsolutePath().split('/')
+ buildDir = (buildDir[0..(buildDir.length - 4)] + ['build', 'classes', 'debug']).join('/')
+
+ sourceSets.testLocal.compileClasspath += files(buildDir)
+ sourceSets.testLocal.runtimeClasspath += files(buildDir)
+ }
+
+ classpath = sourceSets.testLocal.runtimeClasspath
+}
+
+check.dependsOn localTest
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 592bdec73..520189448 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
@@ -17,20 +17,6 @@
package org.sufficientlysecure.keychain.pgp;
-import java.io.IOException;
-import java.math.BigInteger;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.KeyPairGenerator;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.SecureRandom;
-import java.security.SignatureException;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.GregorianCalendar;
-import java.util.Iterator;
-import java.util.TimeZone;
-import android.content.Context;
import org.spongycastle.bcpg.CompressionAlgorithmTags;
import org.spongycastle.bcpg.HashAlgorithmTags;
import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags;
@@ -46,18 +32,11 @@ import org.spongycastle.openpgp.operator.jcajce.*;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.util.Primes;
import org.sufficientlysecure.keychain.util.ProgressDialogUpdater;
-
-import android.content.Context;
-import android.util.Pair;
import org.sufficientlysecure.keychain.util.IterableIterator;
-import org.sufficientlysecure.keychain.util.Log;
-import org.sufficientlysecure.keychain.util.Primes;
-import org.sufficientlysecure.keychain.util.ProgressDialogUpdater;
import java.io.IOException;
import java.math.BigInteger;
@@ -69,9 +48,17 @@ import java.util.Iterator;
import java.util.List;
import java.util.TimeZone;
+/** This class is the single place where ALL operations that actually modify a PGP public or secret
+ * key take place.
+ *
+ * Note that no android specific stuff should be done here, ie no imports from com.android.
+ *
+ * All operations support progress reporting to a ProgressDialogUpdater passed on initialization.
+ * This indicator may be null.
+ *
+ */
public class PgpKeyOperation {
- private final Context mContext;
- private final ProgressDialogUpdater mProgress;
+ private ProgressDialogUpdater mProgress;
private static final int[] PREFERRED_SYMMETRIC_ALGORITHMS = new int[]{
SymmetricKeyAlgorithmTags.AES_256, SymmetricKeyAlgorithmTags.AES_192,
@@ -83,9 +70,8 @@ public class PgpKeyOperation {
CompressionAlgorithmTags.ZLIB, CompressionAlgorithmTags.BZIP2,
CompressionAlgorithmTags.ZIP};
- public PgpKeyOperation(Context context, ProgressDialogUpdater progress) {
+ public PgpKeyOperation(ProgressDialogUpdater progress) {
super();
- this.mContext = context;
this.mProgress = progress;
}
@@ -101,13 +87,29 @@ public class PgpKeyOperation {
}
}
+ /**
+ * Creates new secret key.
+ *
+ * @param algorithmChoice
+ * @param keySize
+ * @param passphrase
+ * @param isMasterKey
+ * @return A newly created PGPSecretKey
+ * @throws NoSuchAlgorithmException
+ * @throws PGPException
+ * @throws NoSuchProviderException
+ * @throws PgpGeneralMsgIdException
+ * @throws InvalidAlgorithmParameterException
+ */
+
+ // TODO: key flags?
public PGPSecretKey createKey(int algorithmChoice, int keySize, String passphrase,
boolean isMasterKey)
throws NoSuchAlgorithmException, PGPException, NoSuchProviderException,
- PgpGeneralException, InvalidAlgorithmParameterException {
+ PgpGeneralMsgIdException, InvalidAlgorithmParameterException {
if (keySize < 512) {
- throw new PgpGeneralException(mContext.getString(R.string.error_key_size_minimum512bit));
+ throw new PgpGeneralMsgIdException(R.string.error_key_size_minimum512bit);
}
if (passphrase == null) {
@@ -127,8 +129,7 @@ public class PgpKeyOperation {
case Id.choice.algorithm.elgamal: {
if (isMasterKey) {
- throw new PgpGeneralException(
- mContext.getString(R.string.error_master_key_must_not_be_el_gamal));
+ throw new PgpGeneralMsgIdException(R.string.error_master_key_must_not_be_el_gamal);
}
keyGen = KeyPairGenerator.getInstance("ElGamal", Constants.BOUNCY_CASTLE_PROVIDER_NAME);
BigInteger p = Primes.getBestPrime(keySize);
@@ -150,8 +151,7 @@ public class PgpKeyOperation {
}
default: {
- throw new PgpGeneralException(
- mContext.getString(R.string.error_unknown_algorithm_choice));
+ throw new PgpGeneralMsgIdException(R.string.error_unknown_algorithm_choice);
}
}
@@ -171,8 +171,9 @@ public class PgpKeyOperation {
sha1Calc, isMasterKey, keyEncryptor);
}
- public void changeSecretKeyPassphrase(PGPSecretKeyRing keyRing, String oldPassPhrase,
- String newPassPhrase) throws IOException, PGPException {
+ public PGPSecretKeyRing changeSecretKeyPassphrase(PGPSecretKeyRing keyRing, String oldPassPhrase,
+ String newPassPhrase) throws IOException, PGPException,
+ NoSuchProviderException {
updateProgress(R.string.progress_building_key, 0, 100);
if (oldPassPhrase == null) {
@@ -190,16 +191,16 @@ public class PgpKeyOperation {
new JcePBESecretKeyEncryptorBuilder(keyRing.getSecretKey()
.getKeyEncryptionAlgorithm()).build(newPassPhrase.toCharArray()));
- updateProgress(R.string.progress_saving_key_ring, 50, 100);
-
- ProviderHelper.saveKeyRing(mContext, newKeyRing);
-
- updateProgress(R.string.progress_done, 100, 100);
+ return newKeyRing;
}
- private void buildNewSecretKey(ArrayList<String> userIds, ArrayList<PGPSecretKey> keys, ArrayList<GregorianCalendar> keysExpiryDates, ArrayList<Integer> keysUsages, String newPassPhrase, String oldPassPhrase) throws PgpGeneralException,
- PGPException, SignatureException, IOException {
+ private Pair<PGPSecretKeyRing,PGPPublicKeyRing> buildNewSecretKey(
+ ArrayList<String> userIds, ArrayList<PGPSecretKey> keys,
+ ArrayList<GregorianCalendar> keysExpiryDates,
+ ArrayList<Integer> keysUsages,
+ String newPassPhrase, String oldPassPhrase)
+ throws PgpGeneralMsgIdException, PGPException, SignatureException, IOException {
int usageId = keysUsages.get(0);
boolean canSign;
@@ -210,8 +211,6 @@ public class PgpKeyOperation {
// this removes all userIds and certifications previously attached to the masterPublicKey
PGPPublicKey masterPublicKey = masterKey.getPublicKey();
- PGPSecretKeyRing mKR = ProviderHelper.getPGPSecretKeyRingByKeyId(mContext, masterKey.getKeyID());
-
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(oldPassPhrase.toCharArray());
PGPPrivateKey masterPrivateKey = masterKey.extractPrivateKey(keyDecryptor);
@@ -249,7 +248,7 @@ public class PgpKeyOperation {
//here we purposefully ignore partial days in each date - long type has no fractional part!
long numDays = (expiryDate.getTimeInMillis() / 86400000) - (creationDate.getTimeInMillis() / 86400000);
if (numDays <= 0)
- throw new PgpGeneralException(mContext.getString(R.string.error_expiry_must_come_after_creation));
+ throw new PgpGeneralMsgIdException(R.string.error_expiry_must_come_after_creation);
hashedPacketsGen.setKeyExpirationTime(false, numDays * 86400);
} else {
hashedPacketsGen.setKeyExpirationTime(false, 0); //do this explicitly, although since we're rebuilding,
@@ -319,9 +318,11 @@ public class PgpKeyOperation {
GregorianCalendar expiryDate = keysExpiryDates.get(i);
//note that the below, (a/c) - (b/c) is *not* the same as (a - b) /c
//here we purposefully ignore partial days in each date - long type has no fractional part!
- long numDays = (expiryDate.getTimeInMillis() / 86400000) - (creationDate.getTimeInMillis() / 86400000);
- if (numDays <= 0)
- throw new PgpGeneralException(mContext.getString(R.string.error_expiry_must_come_after_creation));
+ long numDays =
+ (expiryDate.getTimeInMillis() / 86400000) - (creationDate.getTimeInMillis() / 86400000);
+ if (numDays <= 0) {
+ throw new PgpGeneralMsgIdException(R.string.error_expiry_must_come_after_creation);
+ }
hashedPacketsGen.setKeyExpirationTime(false, numDays * 86400);
} else {
hashedPacketsGen.setKeyExpirationTime(false, 0); //do this explicitly, although since we're rebuilding,
@@ -334,23 +335,18 @@ public class PgpKeyOperation {
PGPSecretKeyRing secretKeyRing = keyGen.generateSecretKeyRing();
PGPPublicKeyRing publicKeyRing = keyGen.generatePublicKeyRing();
- updateProgress(R.string.progress_saving_key_ring, 90, 100);
-
- ProviderHelper.saveKeyRing(mContext, secretKeyRing);
- ProviderHelper.saveKeyRing(mContext, publicKeyRing);
+ return new Pair<PGPSecretKeyRing,PGPPublicKeyRing>(secretKeyRing, publicKeyRing);
- updateProgress(R.string.progress_done, 100, 100);
}
- public void buildSecretKey (SaveKeyringParcel saveParcel)throws PgpGeneralException,
- PGPException, SignatureException, IOException {
+ public Pair<PGPSecretKeyRing,PGPPublicKeyRing> buildSecretKey (PGPSecretKeyRing mKR,
+ PGPPublicKeyRing pKR,
+ SaveKeyringParcel saveParcel)
+ throws PgpGeneralMsgIdException, PGPException, SignatureException, IOException {
updateProgress(R.string.progress_building_key, 0, 100);
PGPSecretKey masterKey = saveParcel.keys.get(0);
- PGPSecretKeyRing mKR = ProviderHelper.getPGPSecretKeyRingByKeyId(mContext, masterKey.getKeyID());
- PGPPublicKeyRing pKR = ProviderHelper.getPGPPublicKeyRingByKeyId(mContext, masterKey.getKeyID());
-
if (saveParcel.oldPassPhrase == null) {
saveParcel.oldPassPhrase = "";
}
@@ -359,9 +355,8 @@ public class PgpKeyOperation {
}
if (mKR == null) {
- buildNewSecretKey(saveParcel.userIDs, saveParcel.keys, saveParcel.keysExpiryDates,
+ return buildNewSecretKey(saveParcel.userIDs, saveParcel.keys, saveParcel.keysExpiryDates,
saveParcel.keysUsages, saveParcel.newPassPhrase, saveParcel.oldPassPhrase); //new Keyring
- return;
}
/*
@@ -430,7 +425,7 @@ public class PgpKeyOperation {
//here we purposefully ignore partial days in each date - long type has no fractional part!
long numDays = (expiryDate.getTimeInMillis() / 86400000) - (creationDate.getTimeInMillis() / 86400000);
if (numDays <= 0)
- throw new PgpGeneralException(mContext.getString(R.string.error_expiry_must_come_after_creation));
+ throw new PgpGeneralMsgIdException(R.string.error_expiry_must_come_after_creation);
hashedPacketsGen.setKeyExpirationTime(false, numDays * 86400);
} else {
hashedPacketsGen.setKeyExpirationTime(false, 0); //do this explicitly, although since we're rebuilding,
@@ -592,7 +587,7 @@ public class PgpKeyOperation {
//here we purposefully ignore partial days in each date - long type has no fractional part!
long numDays = (expiryDate.getTimeInMillis() / 86400000) - (creationDate.getTimeInMillis() / 86400000);
if (numDays <= 0)
- throw new PgpGeneralException(mContext.getString(R.string.error_expiry_must_come_after_creation));
+ throw new PgpGeneralMsgIdException(R.string.error_expiry_must_come_after_creation);
hashedPacketsGen.setKeyExpirationTime(false, numDays * 86400);
} else {
hashedPacketsGen.setKeyExpirationTime(false, 0); //do this explicitly, although since we're rebuilding,
@@ -637,7 +632,6 @@ public class PgpKeyOperation {
//update the passphrase
mKR = PGPSecretKeyRing.copyWithNewPassword(mKR, keyDecryptor, keyEncryptorNew);
- updateProgress(R.string.progress_saving_key_ring, 90, 100);
/* additional handy debug info
@@ -660,62 +654,72 @@ public class PgpKeyOperation {
*/
+ return new Pair<PGPSecretKeyRing,PGPPublicKeyRing>(mKR, pKR);
- ProviderHelper.saveKeyRing(mContext, mKR);
- ProviderHelper.saveKeyRing(mContext, pKR);
-
- updateProgress(R.string.progress_done, 100, 100);
}
- public PGPPublicKeyRing certifyKey(long masterKeyId, long pubKeyId, List<String> userIds, String passphrase)
- throws PgpGeneralException, NoSuchAlgorithmException, NoSuchProviderException,
+ /**
+ * Certify the given pubkeyid with the given masterkeyid.
+ *
+ * @param certificationKey Certifying key
+ * @param publicKey public key to certify
+ * @param userIds User IDs to certify, must not be null or empty
+ * @param passphrase Passphrase of the secret key
+ * @return A keyring with added certifications
+ */
+ public PGPPublicKey certifyKey(PGPSecretKey certificationKey, PGPPublicKey publicKey, List<String> userIds, String passphrase)
+ throws PgpGeneralMsgIdException, NoSuchAlgorithmException, NoSuchProviderException,
PGPException, SignatureException {
- if (passphrase == null) {
- throw new PgpGeneralException("Unable to obtain passphrase");
- } else {
- // create a signatureGenerator from the supplied masterKeyId and passphrase
- PGPSignatureGenerator signatureGenerator; {
+ // create a signatureGenerator from the supplied masterKeyId and passphrase
+ PGPSignatureGenerator signatureGenerator; {
- PGPSecretKey certificationKey = PgpKeyHelper.getCertificationKey(mContext, masterKeyId);
- if (certificationKey == null) {
- throw new PgpGeneralException(mContext.getString(R.string.error_signature_failed));
- }
+ if (certificationKey == null) {
+ throw new PgpGeneralMsgIdException(R.string.error_signature_failed);
+ }
- PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
- Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
- PGPPrivateKey signaturePrivateKey = certificationKey.extractPrivateKey(keyDecryptor);
- if (signaturePrivateKey == null) {
- throw new PgpGeneralException(
- mContext.getString(R.string.error_could_not_extract_private_key));
- }
+ PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
+ Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
+ PGPPrivateKey signaturePrivateKey = certificationKey.extractPrivateKey(keyDecryptor);
+ if (signaturePrivateKey == null) {
+ throw new PgpGeneralMsgIdException(R.string.error_could_not_extract_private_key);
+ }
- // TODO: SHA256 fixed?
- JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(
- certificationKey.getPublicKey().getAlgorithm(), PGPUtil.SHA256)
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ // TODO: SHA256 fixed?
+ JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(
+ certificationKey.getPublicKey().getAlgorithm(), PGPUtil.SHA256)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
- signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
- signatureGenerator.init(PGPSignature.DEFAULT_CERTIFICATION, signaturePrivateKey);
- }
+ signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
+ signatureGenerator.init(PGPSignature.DEFAULT_CERTIFICATION, signaturePrivateKey);
+ }
- { // supply signatureGenerator with a SubpacketVector
- PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
- PGPSignatureSubpacketVector packetVector = spGen.generate();
- signatureGenerator.setHashedSubpackets(packetVector);
- }
+ { // supply signatureGenerator with a SubpacketVector
+ PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
+ PGPSignatureSubpacketVector packetVector = spGen.generate();
+ signatureGenerator.setHashedSubpackets(packetVector);
+ }
- // fetch public key ring, add the certification and return it
- PGPPublicKeyRing pubring = ProviderHelper
- .getPGPPublicKeyRingByKeyId(mContext, pubKeyId);
- PGPPublicKey signedKey = pubring.getPublicKey(pubKeyId);
- for(String userId : new IterableIterator<String>(userIds.iterator())) {
- PGPSignature sig = signatureGenerator.generateCertification(userId, signedKey);
- signedKey = PGPPublicKey.addCertification(signedKey, userId, sig);
- }
- pubring = PGPPublicKeyRing.insertPublicKey(pubring, signedKey);
+ // fetch public key ring, add the certification and return it
+ for(String userId : new IterableIterator<String>(userIds.iterator())) {
+ PGPSignature sig = signatureGenerator.generateCertification(userId, publicKey);
+ publicKey = PGPPublicKey.addCertification(publicKey, userId, sig);
+ }
+
+ return publicKey;
+ }
- return pubring;
+ /** Simple static subclass that stores two values.
+ *
+ * This is only used to return a pair of values in one function above. We specifically don't use
+ * com.android.Pair to keep this class free from android dependencies.
+ */
+ public static class Pair<K, V> {
+ public final K first;
+ public final V second;
+ public Pair(K first, V second) {
+ this.first = first;
+ this.second = second;
}
}
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralException.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralException.java
index bb80d27ee..418445367 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralException.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralException.java
@@ -23,4 +23,7 @@ public class PgpGeneralException extends Exception {
public PgpGeneralException(String message) {
super(message);
}
+ public PgpGeneralException(String message, Throwable cause) {
+ super(message, cause);
+ }
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralMsgIdException.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralMsgIdException.java
new file mode 100644
index 000000000..3df85cfc8
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralMsgIdException.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2012-2013 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.sufficientlysecure.keychain.pgp.exception;
+
+import android.content.Context;
+
+public class PgpGeneralMsgIdException extends Exception {
+ static final long serialVersionUID = 0xf812773343L;
+
+ private final int msgId;
+
+ public PgpGeneralMsgIdException(int msgId) {
+ super("msg[" + msgId + "]");
+ this.msgId = msgId;
+ }
+
+ public PgpGeneralException getContextualized(Context context) {
+ return new PgpGeneralException(context.getString(msgId), this);
+ }
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
index 2060c6f61..f82e5e619 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
@@ -35,6 +35,8 @@ import org.sufficientlysecure.keychain.helper.OtherHelper;
import org.sufficientlysecure.keychain.helper.Preferences;
import org.sufficientlysecure.keychain.pgp.*;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException;
+import org.sufficientlysecure.keychain.provider.KeychainContract.DataStream;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry;
import org.sufficientlysecure.keychain.util.*;
@@ -473,14 +475,26 @@ public class KeychainIntentService extends IntentService
long masterKeyId = saveParams.keys.get(0).getKeyID();
- PgpKeyOperation keyOperations = new PgpKeyOperation(this, this);
/* Operation */
if (!canSign) {
- keyOperations.changeSecretKeyPassphrase(
- ProviderHelper.getPGPSecretKeyRingByKeyId(this, masterKeyId),
+ PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 50, 100));
+ PGPSecretKeyRing keyRing = ProviderHelper
+ .getPGPSecretKeyRingByKeyId(this, masterKeyId);
+ keyRing = keyOperations.changeSecretKeyPassphrase(keyRing,
oldPassPhrase, newPassPhrase);
+ setProgress(R.string.progress_saving_key_ring, 50, 100);
+ ProviderHelper.saveKeyRing(this, keyRing);
+ setProgress(R.string.progress_done, 100, 100);
} else {
- keyOperations.buildSecretKey(saveParams);
+ PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 90, 100));
+ PGPSecretKeyRing privkey = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(this, masterKeyId);
+ PGPPublicKeyRing pubkey = ProviderHelper.getPGPPublicKeyRingByMasterKeyId(this, masterKeyId);
+ PgpKeyOperation.Pair<PGPSecretKeyRing,PGPPublicKeyRing> pair =
+ keyOperations.buildSecretKey(privkey, pubkey, saveParams);
+ setProgress(R.string.progress_saving_key_ring, 90, 100);
+ ProviderHelper.saveKeyRing(this, pair.first);
+ ProviderHelper.saveKeyRing(this, pair.second);
+ setProgress(R.string.progress_done, 100, 100);
}
PassphraseCacheService.addCachedPassphrase(this, masterKeyId, newPassPhrase);
@@ -498,7 +512,7 @@ public class KeychainIntentService extends IntentService
boolean masterKey = data.getBoolean(GENERATE_KEY_MASTER_KEY);
/* Operation */
- PgpKeyOperation keyOperations = new PgpKeyOperation(this, this);
+ PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 100, 100));
PGPSecretKey newKey = keyOperations.createKey(algorithm, keysize,
passphrase, masterKey);
@@ -529,7 +543,7 @@ public class KeychainIntentService extends IntentService
getQuantityString(R.plurals.progress_generating, keysTotal),
keysCreated,
keysTotal);
- PgpKeyOperation keyOperations = new PgpKeyOperation(this, this);
+ PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 100, 100));
PGPSecretKey masterKey = keyOperations.createKey(Id.choice.algorithm.rsa,
4096, passphrase, true);
@@ -771,14 +785,23 @@ public class KeychainIntentService extends IntentService
/* Operation */
String signaturePassPhrase = PassphraseCacheService.getCachedPassphrase(this,
masterKeyId);
+ if (signaturePassPhrase == null) {
+ throw new PgpGeneralException("Unable to obtain passphrase");
+ }
- PgpKeyOperation keyOperation = new PgpKeyOperation(this, this);
- PGPPublicKeyRing signedPubKeyRing = keyOperation.certifyKey(masterKeyId, pubKeyId,
+ PgpKeyOperation keyOperation = new PgpKeyOperation(new ProgressScaler(this, 0, 100, 100));
+ PGPPublicKeyRing publicRing = ProviderHelper
+ .getPGPPublicKeyRingByKeyId(this, pubKeyId);
+ PGPPublicKey publicKey = publicRing.getPublicKey(pubKeyId);
+ PGPSecretKey certificationKey = PgpKeyHelper.getCertificationKey(this,
+ masterKeyId);
+ publicKey = keyOperation.certifyKey(certificationKey, publicKey,
userIds, signaturePassPhrase);
+ publicRing = PGPPublicKeyRing.insertPublicKey(publicRing, publicKey);
// store the signed key in our local cache
PgpImportExport pgpImportExport = new PgpImportExport(this, null);
- int retval = pgpImportExport.storeKeyRingInCache(signedPubKeyRing);
+ int retval = pgpImportExport.storeKeyRingInCache(publicRing);
if (retval != Id.return_value.ok && retval != Id.return_value.updated) {
throw new PgpGeneralException("Failed to store signed key in local cache");
}
@@ -795,7 +818,11 @@ public class KeychainIntentService extends IntentService
if (this.mIsCanceled) {
return;
}
- Log.e(Constants.TAG, "KeychainIntentService Exception: ", e);
+ // contextualize the exception, if necessary
+ if(e instanceof PgpGeneralMsgIdException) {
+ e = ((PgpGeneralMsgIdException) e).getContextualized(this);
+ }
+ Log.e(Constants.TAG, "ApgService Exception: ", e);
e.printStackTrace();
Bundle data = new Bundle();
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/ProgressScaler.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/ProgressScaler.java
new file mode 100644
index 000000000..15b70e22e
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/ProgressScaler.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2012-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.util;
+
+/** This is a simple class that wraps a ProgressDialogUpdater, scaling the progress
+ * values into a specified range.
+ */
+public class ProgressScaler implements ProgressDialogUpdater {
+
+ final ProgressDialogUpdater mWrapped;
+ final int mFrom, mTo, mMax;
+
+ public ProgressScaler(ProgressDialogUpdater wrapped, int from, int to, int max) {
+ this.mWrapped = wrapped;
+ this.mFrom = from;
+ this.mTo = to;
+ this.mMax = max;
+ }
+
+ /**
+ * Set progressDialogUpdater of ProgressDialog by sending message to handler on UI thread
+ */
+ public void setProgress(String message, int progress, int max) {
+ mWrapped.setProgress(message, mFrom+ progress*(mTo-mFrom)/max, mMax);
+ }
+
+ public void setProgress(int resourceId, int progress, int max) {
+ mWrapped.setProgress(resourceId, progress, mMax);
+ }
+
+ public void setProgress(int progress, int max) {
+ mWrapped.setProgress(progress, max);
+ }
+
+}
diff --git a/OpenPGP-Keychain/src/main/res/layout/key_server_preference.xml b/OpenPGP-Keychain/src/main/res/layout/key_server_preference.xml
index eddbe3cbf..b8897a7b3 100644
--- a/OpenPGP-Keychain/src/main/res/layout/key_server_preference.xml
+++ b/OpenPGP-Keychain/src/main/res/layout/key_server_preference.xml
@@ -6,7 +6,7 @@
android:orientation="vertical" >
<LinearLayout
- android:id="@+android:id/text_layout"
+ android:id="@+id/text_layout"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
diff --git a/OpenPGP-Keychain/src/test/java/org/sufficientlysecure/keychain/PgpKeyOperationTest.java b/OpenPGP-Keychain/src/test/java/org/sufficientlysecure/keychain/PgpKeyOperationTest.java
new file mode 100644
index 000000000..72f29a1e3
--- /dev/null
+++ b/OpenPGP-Keychain/src/test/java/org/sufficientlysecure/keychain/PgpKeyOperationTest.java
@@ -0,0 +1,46 @@
+package org.sufficientlysecure.keychain;
+
+import org.junit.Before;
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.Robolectric;
+
+import org.sufficientlysecure.keychain.pgp.*;
+import org.spongycastle.openpgp.*;
+
+@RunWith(RobolectricGradleTestRunner.class)
+public class PgpKeyOperationTest {
+
+ PGPSecretKey key;
+
+ @Before
+ public void setUp() throws Exception {
+
+ /* Input */
+ int algorithm = Id.choice.algorithm.dsa;
+ String passphrase = "swag";
+ int keysize = 2048;
+ boolean masterKey = true;
+
+ /* Operation */
+ PgpKeyOperation keyOperations = new PgpKeyOperation(null);
+ key = keyOperations.createKey(algorithm, keysize, passphrase, masterKey);
+
+ System.err.println("initialized, test key: " + PgpKeyHelper.convertKeyIdToHex(key.getKeyID()));
+ }
+
+ @After
+ public void tearDown() {
+ }
+
+ @Test
+ public void createTest() {
+ }
+
+ @Test
+ public void certifyKey() {
+ System.err.println("swag");
+ }
+
+}
diff --git a/OpenPGP-Keychain/src/test/java/org/sufficientlysecure/keychain/RobolectricGradleTestRunner.java b/OpenPGP-Keychain/src/test/java/org/sufficientlysecure/keychain/RobolectricGradleTestRunner.java
new file mode 100644
index 000000000..b64ffde07
--- /dev/null
+++ b/OpenPGP-Keychain/src/test/java/org/sufficientlysecure/keychain/RobolectricGradleTestRunner.java
@@ -0,0 +1,23 @@
+package org.sufficientlysecure.keychain;
+
+import org.junit.runners.model.InitializationError;
+import org.robolectric.AndroidManifest;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.robolectric.res.Fs;
+import org.robolectric.res.FsFile;
+
+import org.sufficientlysecure.keychain.KeychainApplication;
+
+public class RobolectricGradleTestRunner extends RobolectricTestRunner {
+ public RobolectricGradleTestRunner(Class<?> testClass) throws InitializationError {
+ super(testClass);
+ }
+
+ @Override protected AndroidManifest getAppManifest(Config config) {
+ String myAppPath = KeychainApplication.class.getProtectionDomain().getCodeSource().getLocation().getPath();
+ String manifestPath = myAppPath + "../../../src/main/AndroidManifest.xml";
+ return createAppManifest(Fs.fileFromPath(manifestPath));
+ }
+}
+