aboutsummaryrefslogtreecommitdiffstats
path: root/OpenKeychain
diff options
context:
space:
mode:
Diffstat (limited to 'OpenKeychain')
-rw-r--r--OpenKeychain/build.gradle59
-rw-r--r--OpenKeychain/src/androidTest/java/tests/SomeTest.java12
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ExportHelper.java6
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java70
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableKeyRing.java51
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java36
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConversionHelper.java109
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java274
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java124
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java235
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java346
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java85
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java171
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java197
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedSecretKey.java33
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedKeyRing.java97
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedPublicKey.java39
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedPublicKeyRing.java192
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKey.java200
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKeyRing.java141
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java161
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralException.java3
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java161
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java22
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java34
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java82
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java339
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java6
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java197
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OldSaveKeyringParcel.java128
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java96
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java158
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java64
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java51
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java32
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java9
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java23
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyLayoutFragment.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java72
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java8
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java12
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java10
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyShareFragment.java4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java3
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java48
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java134
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java80
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java19
-rw-r--r--OpenKeychain/src/main/res/values/strings.xml1
-rw-r--r--OpenKeychain/src/test/java/org/sufficientlysecure/keychain/PgpKeyOperationTest.java46
-rw-r--r--OpenKeychain/src/test/java/org/sufficientlysecure/keychain/RobolectricGradleTestRunner.java23
54 files changed, 2724 insertions, 1789 deletions
diff --git a/OpenKeychain/build.gradle b/OpenKeychain/build.gradle
index 090a7a2bf..11dd7f21c 100644
--- a/OpenKeychain/build.gradle
+++ b/OpenKeychain/build.gradle
@@ -1,9 +1,11 @@
apply plugin: 'android'
+apply plugin: 'android-test'
sourceSets {
- testLocal {
+ androidTest {
java.srcDir file('src/test/java')
- resources.srcDir file('src/test/resources')
+ // configure the set of classes for JUnit tests
+ // include '**/*Test.class'
}
}
@@ -23,24 +25,25 @@ dependencies {
compile project(':extern:spongycastle:prov')
compile project(':extern: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.2'
- testLocalCompile 'com.google.android:android:4.1.1.4'
- testLocalCompile 'com.android.support:support-v4:19.1.0'
- testLocalCompile 'com.android.support:appcompat-v7:19.1.0'
- testLocalCompile project(':extern:openpgp-api-lib')
- testLocalCompile project(':extern:openkeychain-api-lib')
- testLocalCompile project(':extern:html-textview')
- testLocalCompile project(':extern:StickyListHeaders:library')
- testLocalCompile project(':extern:AndroidBootstrap:AndroidBootstrap')
- testLocalCompile project(':extern:zxing-qr-code')
- testLocalCompile project(':extern:zxing-android-integration')
- testLocalCompile project(':extern:spongycastle:core')
- testLocalCompile project(':extern:spongycastle:pg')
- testLocalCompile project(':extern:spongycastle:pkix')
- testLocalCompile project(':extern:spongycastle:prov')
- testLocalCompile project(':extern:AppMsg:library')
+ // Dependencies for the `instrumentTest` task, make sure to list all your global dependencies here as well
+ androidTestCompile 'junit:junit:4.10'
+ androidTestCompile 'org.robolectric:robolectric:2.1.+'
+ androidTestCompile 'com.squareup:fest-android:1.0.+'
+ androidTestCompile 'com.google.android:android:4.1.1.4'
+ androidTestCompile 'com.android.support:support-v4:19.1.0'
+ androidTestCompile 'com.android.support:appcompat-v7:19.1.0'
+ androidTestCompile project(':extern:openpgp-api-lib')
+ androidTestCompile project(':extern:openkeychain-api-lib')
+ androidTestCompile project(':extern:html-textview')
+ androidTestCompile project(':extern:StickyListHeaders:library')
+ androidTestCompile project(':extern:AndroidBootstrap:AndroidBootstrap')
+ androidTestCompile project(':extern:zxing-qr-code')
+ androidTestCompile project(':extern:zxing-android-integration')
+ androidTestCompile project(':extern:spongycastle:core')
+ androidTestCompile project(':extern:spongycastle:pg')
+ androidTestCompile project(':extern:spongycastle:pkix')
+ androidTestCompile project(':extern:spongycastle:prov')
+ androidTestCompile project(':extern:AppMsg:library')
}
android {
@@ -92,20 +95,6 @@ android {
}
}
-task localTest(type: Test, dependsOn: assemble) {
- testClassesDir = sourceSets.testLocal.output.classesDir
-
- android.sourceSets.main.java.srcDirs.each { dir ->
- def buildDir = dir.getAbsolutePath().split("\\" + File.separator)
- buildDir = (buildDir[0..(buildDir.length - 4)] + ['build', 'classes', 'debug']).join("\\" + File.separator)
-
- sourceSets.testLocal.compileClasspath += files(buildDir)
- sourceSets.testLocal.runtimeClasspath += files(buildDir)
- }
-
- classpath = sourceSets.testLocal.runtimeClasspath
-}
-
// NOTE: This disables Lint!
tasks.whenTaskAdded { task ->
if (task.name.equals("lint")) {
@@ -113,5 +102,3 @@ tasks.whenTaskAdded { task ->
}
}
-// NOTE: tests disabled!
-//check.dependsOn localTest
diff --git a/OpenKeychain/src/androidTest/java/tests/SomeTest.java b/OpenKeychain/src/androidTest/java/tests/SomeTest.java
new file mode 100644
index 000000000..edf5f7bcc
--- /dev/null
+++ b/OpenKeychain/src/androidTest/java/tests/SomeTest.java
@@ -0,0 +1,12 @@
+package tests;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class SomeTest {
+ @Test
+ public void willFail() {
+ // stub
+ // Assert.assertThat();
+ }
+} \ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ExportHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ExportHelper.java
index 31b88856f..16ef28311 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ExportHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ExportHelper.java
@@ -31,6 +31,7 @@ import android.widget.Toast;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
@@ -51,14 +52,15 @@ public class ExportHelper {
public void deleteKey(Uri dataUri, Handler deleteHandler) {
try {
- long masterKeyId = new ProviderHelper(mActivity).extractOrGetMasterKeyId(dataUri);
+ long masterKeyId = new ProviderHelper(mActivity).getCachedPublicKeyRing(dataUri)
+ .extractOrGetMasterKeyId();
// Create a new Messenger for the communication back
Messenger messenger = new Messenger(deleteHandler);
DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger,
new long[]{ masterKeyId });
deleteKeyDialog.show(mActivity.getSupportFragmentManager(), "deleteKeyDialog");
- } catch (ProviderHelper.NotFoundException e) {
+ } catch (PgpGeneralException e) {
Log.e(Constants.TAG, "key not found!", e);
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java
index 04b86e295..c43f72235 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java
@@ -21,18 +21,10 @@ import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
-import org.spongycastle.bcpg.SignatureSubpacketTags;
-import org.spongycastle.openpgp.PGPKeyRing;
-import org.spongycastle.openpgp.PGPPublicKey;
-import org.spongycastle.openpgp.PGPSecretKeyRing;
-import org.spongycastle.openpgp.PGPSignature;
-import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
-import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
-import org.sufficientlysecure.keychain.util.IterableIterator;
-import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
+import org.sufficientlysecure.keychain.pgp.UncachedPublicKey;
-import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
@@ -55,8 +47,6 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
private boolean mSelected;
- private byte[] mBytes = new byte[]{};
-
public int describeContents() {
return 0;
}
@@ -74,8 +64,6 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
dest.writeString(algorithm);
dest.writeByte((byte) (secretKey ? 1 : 0));
dest.writeByte((byte) (mSelected ? 1 : 0));
- dest.writeInt(mBytes.length);
- dest.writeByteArray(mBytes);
dest.writeString(mExtraData);
}
@@ -94,8 +82,6 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
vr.algorithm = source.readString();
vr.secretKey = source.readByte() == 1;
vr.mSelected = source.readByte() == 1;
- vr.mBytes = new byte[source.readInt()];
- source.readByteArray(vr.mBytes);
vr.mExtraData = source.readString();
return vr;
@@ -110,14 +96,6 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
return keyIdHex;
}
- public byte[] getBytes() {
- return mBytes;
- }
-
- public void setBytes(byte[] bytes) {
- this.mBytes = bytes;
- }
-
public boolean isSelected() {
return mSelected;
}
@@ -233,53 +211,25 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
* Constructor based on key object, used for import from NFC, QR Codes, files
*/
@SuppressWarnings("unchecked")
- public ImportKeysListEntry(Context context, PGPKeyRing pgpKeyRing) {
- // save actual key object into entry, used to import it later
- try {
- this.mBytes = pgpKeyRing.getEncoded();
- } catch (IOException e) {
- Log.e(Constants.TAG, "IOException on pgpKeyRing.getEncoded()", e);
- }
-
+ public ImportKeysListEntry(Context context, UncachedKeyRing ring) {
// selected is default
this.mSelected = true;
- if (pgpKeyRing instanceof PGPSecretKeyRing) {
- secretKey = true;
- } else {
- secretKey = false;
- }
- PGPPublicKey key = pgpKeyRing.getPublicKey();
+ secretKey = ring.isSecret();
+ UncachedPublicKey key = ring.getPublicKey();
+
+ mPrimaryUserId = key.getPrimaryUserId();
+ userIds = key.getUnorderedUserIds();
- userIds = new ArrayList<String>();
- for (String userId : new IterableIterator<String>(key.getUserIDs())) {
- userIds.add(userId);
- for (PGPSignature sig : new IterableIterator<PGPSignature>(key.getSignaturesForID(userId))) {
- if (sig.getHashedSubPackets() != null
- && sig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.PRIMARY_USER_ID)) {
- try {
- // make sure it's actually valid
- sig.init(new JcaPGPContentVerifierBuilderProvider().setProvider(
- Constants.BOUNCY_CASTLE_PROVIDER_NAME), key);
- if (sig.verifyCertification(userId, key)) {
- mPrimaryUserId = userId;
- }
- } catch (Exception e) {
- // nothing bad happens, the key is just not considered the primary key id
- }
- }
-
- }
- }
// if there was no user id flagged as primary, use the first one
if (mPrimaryUserId == null) {
mPrimaryUserId = userIds.get(0);
}
- this.keyId = key.getKeyID();
+ this.keyId = key.getKeyId();
this.keyIdHex = PgpKeyHelper.convertKeyIdToHex(keyId);
- this.revoked = key.isRevoked();
+ this.revoked = key.maybeRevoked();
this.fingerprintHex = PgpKeyHelper.convertFingerprintToHex(key.getFingerprint());
this.bitStrength = key.getBitStrength();
final int algorithm = key.getAlgorithm();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableKeyRing.java
new file mode 100644
index 000000000..3d3b6339a
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableKeyRing.java
@@ -0,0 +1,51 @@
+package org.sufficientlysecure.keychain.keyimport;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+
+import java.io.IOException;
+
+/** This is a trivial wrapper around UncachedKeyRing which implements Parcelable. It exists
+ * for the sole purpose of keeping spongycastle and android imports in separate packages.
+ */
+public class ParcelableKeyRing implements Parcelable {
+
+ final byte[] mBytes;
+ final String mExpectedFingerprint;
+
+ public ParcelableKeyRing(byte[] bytes) {
+ mBytes = bytes;
+ mExpectedFingerprint = null;
+ }
+ public ParcelableKeyRing(byte[] bytes, String expectedFingerprint) {
+ mBytes = bytes;
+ mExpectedFingerprint = expectedFingerprint;
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeByteArray(mBytes);
+ dest.writeString(mExpectedFingerprint);
+ }
+
+ public static final Creator<ParcelableKeyRing> CREATOR = new Creator<ParcelableKeyRing>() {
+ public ParcelableKeyRing createFromParcel(final Parcel source) {
+ return new ParcelableKeyRing(source.createByteArray());
+ }
+
+ public ParcelableKeyRing[] newArray(final int size) {
+ return new ParcelableKeyRing[size];
+ }
+ };
+
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public UncachedKeyRing getUncachedKeyRing() throws PgpGeneralException, IOException {
+ return UncachedKeyRing.decodeFromData(mBytes);
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java
new file mode 100644
index 000000000..dc0c722b9
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java
@@ -0,0 +1,36 @@
+package org.sufficientlysecure.keychain.pgp;
+
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+
+/** An abstract KeyRing.
+ *
+ * This is an abstract class for all KeyRing constructs. It serves as a common
+ * denominator of available information, two implementations wrapping the same
+ * keyring should in all cases agree on the output of all methods described
+ * here.
+ *
+ * @see org.sufficientlysecure.keychain.pgp.WrappedKeyRing
+ * @see org.sufficientlysecure.keychain.provider.CachedPublicKeyRing
+ *
+ */
+public abstract class KeyRing {
+
+ abstract public long getMasterKeyId() throws PgpGeneralException;
+
+ abstract public String getPrimaryUserId() throws PgpGeneralException;
+
+ abstract public boolean isRevoked() throws PgpGeneralException;
+
+ abstract public boolean canCertify() throws PgpGeneralException;
+
+ abstract public long getEncryptId() throws PgpGeneralException;
+
+ abstract public boolean hasEncrypt() throws PgpGeneralException;
+
+ abstract public long getSignId() throws PgpGeneralException;
+
+ abstract public boolean hasSign() throws PgpGeneralException;
+
+ abstract public int getVerified() throws PgpGeneralException;
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConversionHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConversionHelper.java
index 86fba979c..591ccdc8e 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConversionHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConversionHelper.java
@@ -21,49 +21,25 @@ import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPObjectFactory;
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
-import org.spongycastle.openpgp.PGPSignature;
-import org.spongycastle.openpgp.PGPSignatureList;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.util.Log;
-import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
-
public class PgpConversionHelper {
/**
- * Convert from byte[] to PGPKeyRing
- *
- * @param keysBytes
- * @return
- */
- public static PGPKeyRing BytesToPGPKeyRing(byte[] keysBytes) {
- PGPObjectFactory factory = new PGPObjectFactory(keysBytes);
- PGPKeyRing keyRing = null;
- try {
- if ((keyRing = (PGPKeyRing) factory.nextObject()) == null) {
- Log.e(Constants.TAG, "No keys given!");
- }
- } catch (IOException e) {
- Log.e(Constants.TAG, "Error while converting to PGPKeyRing!", e);
- }
-
- return keyRing;
- }
-
- /**
* Convert from byte[] to ArrayList<PGPSecretKey>
*
* @param keysBytes
* @return
*/
- public static ArrayList<PGPSecretKey> BytesToPGPSecretKeyList(byte[] keysBytes) {
+ public static ArrayList<UncachedSecretKey> BytesToPGPSecretKeyList(byte[] keysBytes) {
PGPObjectFactory factory = new PGPObjectFactory(keysBytes);
Object obj = null;
- ArrayList<PGPSecretKey> keys = new ArrayList<PGPSecretKey>();
+ ArrayList<UncachedSecretKey> keys = new ArrayList<UncachedSecretKey>();
try {
while ((obj = factory.nextObject()) != null) {
PGPSecretKey secKey = null;
@@ -72,7 +48,7 @@ public class PgpConversionHelper {
if (secKey == null) {
Log.e(Constants.TAG, "No keys given!");
}
- keys.add(secKey);
+ keys.add(new UncachedSecretKey(secKey));
} else if (obj instanceof PGPSecretKeyRing) { //master keys are sent as keyrings
PGPSecretKeyRing keyRing = null;
keyRing = (PGPSecretKeyRing) obj;
@@ -82,7 +58,7 @@ public class PgpConversionHelper {
@SuppressWarnings("unchecked")
Iterator<PGPSecretKey> itr = keyRing.getSecretKeys();
while (itr.hasNext()) {
- keys.add(itr.next());
+ keys.add(new UncachedSecretKey(itr.next()));
}
}
}
@@ -100,7 +76,7 @@ public class PgpConversionHelper {
* @param keyBytes
* @return
*/
- public static PGPSecretKey BytesToPGPSecretKey(byte[] keyBytes) {
+ public static UncachedSecretKey BytesToPGPSecretKey(byte[] keyBytes) {
PGPObjectFactory factory = new PGPObjectFactory(keyBytes);
Object obj = null;
try {
@@ -121,80 +97,7 @@ public class PgpConversionHelper {
secKey = keyRing.getSecretKey();
}
- return secKey;
- }
-
- /**
- * Convert from byte[] to PGPSignature
- *
- * @param sigBytes
- * @return
- */
- public static PGPSignature BytesToPGPSignature(byte[] sigBytes) {
- PGPObjectFactory factory = new PGPObjectFactory(sigBytes);
- PGPSignatureList signatures = null;
- try {
- if ((signatures = (PGPSignatureList) factory.nextObject()) == null || signatures.isEmpty()) {
- Log.e(Constants.TAG, "No signatures given!");
- return null;
- }
- } catch (IOException e) {
- Log.e(Constants.TAG, "Error while converting to PGPSignature!", e);
- return null;
- }
-
- return signatures.get(0);
- }
-
- /**
- * Convert from ArrayList<PGPSecretKey> to byte[]
- *
- * @param keys
- * @return
- */
- public static byte[] PGPSecretKeyArrayListToBytes(ArrayList<PGPSecretKey> keys) {
- ByteArrayOutputStream os = new ByteArrayOutputStream();
- for (PGPSecretKey key : keys) {
- try {
- key.encode(os);
- } catch (IOException e) {
- Log.e(Constants.TAG, "Error while converting ArrayList<PGPSecretKey> to byte[]!", e);
- }
- }
-
- return os.toByteArray();
- }
-
- /**
- * Convert from PGPSecretKey to byte[]
- *
- * @param key
- * @return
- */
- public static byte[] PGPSecretKeyToBytes(PGPSecretKey key) {
- try {
- return key.getEncoded();
- } catch (IOException e) {
- Log.e(Constants.TAG, "Encoding failed", e);
-
- return null;
- }
- }
-
- /**
- * Convert from PGPSecretKeyRing to byte[]
- *
- * @param keyRing
- * @return
- */
- public static byte[] PGPSecretKeyRingToBytes(PGPSecretKeyRing keyRing) {
- try {
- return keyRing.getEncoded();
- } catch (IOException e) {
- Log.e(Constants.TAG, "Encoding failed", e);
-
- return null;
- }
+ return new UncachedSecretKey(secKey);
}
}
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 506c161ba..c009d1b5c 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
@@ -18,10 +18,7 @@
package org.sufficientlysecure.keychain.pgp;
-import android.net.Uri;
-
import org.spongycastle.bcpg.ArmoredInputStream;
-import org.spongycastle.bcpg.SignatureSubpacketTags;
import org.spongycastle.openpgp.PGPCompressedData;
import org.spongycastle.openpgp.PGPEncryptedData;
import org.spongycastle.openpgp.PGPEncryptedDataList;
@@ -31,29 +28,19 @@ import org.spongycastle.openpgp.PGPObjectFactory;
import org.spongycastle.openpgp.PGPOnePassSignature;
import org.spongycastle.openpgp.PGPOnePassSignatureList;
import org.spongycastle.openpgp.PGPPBEEncryptedData;
-import org.spongycastle.openpgp.PGPPrivateKey;
-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;
import org.spongycastle.openpgp.PGPUtil;
import org.spongycastle.openpgp.operator.PBEDataDecryptorFactory;
-import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.spongycastle.openpgp.operator.PGPDigestCalculatorProvider;
import org.spongycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePBEDataDecryptorFactoryBuilder;
-import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
-import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.pgp.Progressable;
-import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.util.InputData;
@@ -67,7 +54,6 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.security.SignatureException;
import java.util.Iterator;
-import java.util.Map;
import java.util.Set;
/**
@@ -248,7 +234,7 @@ public class PgpDecryptVerify {
PGPPublicKeyEncryptedData encryptedDataAsymmetric = null;
PGPPBEEncryptedData encryptedDataSymmetric = null;
- PGPSecretKey secretEncryptionKey = null;
+ WrappedSecretKey secretEncryptionKey = null;
Iterator<?> it = enc.getEncryptedDataObjects();
boolean asymmetricPacketFound = false;
boolean symmetricPacketFound = false;
@@ -260,15 +246,12 @@ public class PgpDecryptVerify {
PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) obj;
- long masterKeyId;
- PGPSecretKeyRing secretKeyRing;
+ WrappedSecretKeyRing secretKeyRing;
try {
- // get master key id for this encryption key id
- masterKeyId = mProviderHelper.getMasterKeyId(
- KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(Long.toString(encData.getKeyID()))
- );
// get actual keyring object based on master key id
- secretKeyRing = mProviderHelper.getPGPSecretKeyRing(masterKeyId);
+ secretKeyRing = mProviderHelper.getWrappedSecretKeyRing(
+ KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(encData.getKeyID())
+ );
} catch (ProviderHelper.NotFoundException e) {
// continue with the next packet in the while loop
continue;
@@ -278,13 +261,14 @@ public class PgpDecryptVerify {
continue;
}
// get subkey which has been used for this encryption packet
- secretEncryptionKey = secretKeyRing.getSecretKey(encData.getKeyID());
+ secretEncryptionKey = secretKeyRing.getSubKey(encData.getKeyID());
if (secretEncryptionKey == null) {
// continue with the next packet in the while loop
continue;
}
/* secret key exists in database! */
+ long masterKeyId = secretEncryptionKey.getRing().getMasterKeyId();
// allow only specific keys for decryption?
if (mAllowedKeyIds != null) {
@@ -359,23 +343,17 @@ public class PgpDecryptVerify {
} else if (asymmetricPacketFound) {
currentProgress += 5;
updateProgress(R.string.progress_extracting_key, currentProgress, 100);
- PGPPrivateKey privateKey;
try {
- PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
- mPassphrase.toCharArray());
- privateKey = secretEncryptionKey.extractPrivateKey(keyDecryptor);
- } catch (PGPException e) {
- throw new WrongPassphraseException();
- }
- if (privateKey == null) {
+ if (!secretEncryptionKey.unlock(mPassphrase)) {
+ throw new WrongPassphraseException();
+ }
+ } catch(PgpGeneralException e) {
throw new KeyExtractionException();
}
currentProgress += 5;
updateProgress(R.string.progress_preparing_streams, currentProgress, 100);
- PublicKeyDataDecryptorFactory decryptorFactory = new JcePublicKeyDataDecryptorFactoryBuilder()
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(privateKey);
+ PublicKeyDataDecryptorFactory decryptorFactory = secretEncryptionKey.getDecryptorFactory();
clear = encryptedDataAsymmetric.getDataStream(decryptorFactory);
@@ -388,10 +366,10 @@ public class PgpDecryptVerify {
PGPObjectFactory plainFact = new PGPObjectFactory(clear);
Object dataChunk = plainFact.nextObject();
- PGPOnePassSignature signature = null;
OpenPgpSignatureResultBuilder signatureResultBuilder = new OpenPgpSignatureResultBuilder();
- PGPPublicKey signatureKey = null;
int signatureIndex = -1;
+ WrappedPublicKeyRing signingRing = null;
+ WrappedPublicKey signingKey = null;
if (dataChunk instanceof PGPCompressedData) {
updateProgress(R.string.progress_decompressing_data, currentProgress, 100);
@@ -403,6 +381,8 @@ public class PgpDecryptVerify {
currentProgress += 10;
}
+ PGPOnePassSignature signature = null;
+
if (dataChunk instanceof PGPOnePassSignatureList) {
updateProgress(R.string.progress_processing_signature, currentProgress, 100);
@@ -410,19 +390,13 @@ public class PgpDecryptVerify {
// go through all signatures
// and find out for which signature we have a key in our database
- Long masterKeyId = null;
- String primaryUserId = null;
for (int i = 0; i < sigList.size(); ++i) {
try {
- Uri uri = KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(
- Long.toString(sigList.get(i).getKeyID()));
- Map<String, Object> data = mProviderHelper.getGenericData(uri,
- new String[] { KeyRings.MASTER_KEY_ID, KeyRings.USER_ID },
- new int[] { ProviderHelper.FIELD_TYPE_INTEGER,
- ProviderHelper.FIELD_TYPE_STRING }
+ long sigKeyId = sigList.get(i).getKeyID();
+ signingRing = mProviderHelper.getWrappedPublicKeyRing(
+ KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(sigKeyId)
);
- masterKeyId = (Long) data.get(KeyRings.MASTER_KEY_ID);
- primaryUserId = (String) data.get(KeyRings.USER_ID);
+ signingKey = signingRing.getSubkey(sigKeyId);
signatureIndex = i;
} catch (ProviderHelper.NotFoundException e) {
Log.d(Constants.TAG, "key not found!");
@@ -430,43 +404,24 @@ public class PgpDecryptVerify {
}
}
- if (masterKeyId != null) {
+ if (signingKey != null) {
// key found in our database!
signature = sigList.get(signatureIndex);
- PGPPublicKeyRing publicKeyRing = null;
- try {
- publicKeyRing = mProviderHelper
- .getPGPPublicKeyRing(masterKeyId);
- } catch (ProviderHelper.NotFoundException e) {
- // can't happen
- }
-
- // get the subkey which has been used to generate this signature
- signatureKey = publicKeyRing.getPublicKey(signature.getKeyID());
-
signatureResultBuilder.signatureAvailable(true);
signatureResultBuilder.knownKey(true);
- signatureResultBuilder.userId(primaryUserId);
- signatureResultBuilder.keyId(masterKeyId);
+ signatureResultBuilder.keyId(signingRing.getMasterKeyId());
+ try {
+ signatureResultBuilder.userId(signingRing.getPrimaryUserId());
+ } catch(PgpGeneralException e) {
+ Log.d(Constants.TAG, "No primary user id in key " + signingRing.getMasterKeyId());
+ }
+ signatureResultBuilder.signatureKeyCertified(signingRing.getVerified() > 0);
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
new JcaPGPContentVerifierBuilderProvider()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
- signature.init(contentVerifierBuilderProvider, signatureKey);
-
- // get certification status of this key
- boolean isSignatureKeyCertified;
- try {
- Object data = mProviderHelper.getGenericData(
- KeychainContract.KeyRings.buildUnifiedKeyRingUri(Long.toString(masterKeyId)),
- KeyRings.VERIFIED,
- ProviderHelper.FIELD_TYPE_INTEGER);
- isSignatureKeyCertified = ((Long) data > 0);
- } catch (ProviderHelper.NotFoundException e) {
- isSignatureKeyCertified = false;
- }
- signatureResultBuilder.signatureKeyCertified(isSignatureKeyCertified);
+ signature.init(contentVerifierBuilderProvider, signingKey.getPublicKey());
} else {
// no key in our database -> return "unknown pub key" status including the first key id
if (!sigList.isEmpty()) {
@@ -541,7 +496,7 @@ public class PgpDecryptVerify {
// Verify signature and check binding signatures
boolean validSignature = signature.verify(messageSignature);
- boolean validKeyBinding = verifyKeyBinding(messageSignature, signatureKey);
+ boolean validKeyBinding = signingRing.verifySubkeyBinding(signingKey);
signatureResultBuilder.validSignature(validSignature);
signatureResultBuilder.validKeyBinding(validKeyBinding);
@@ -617,22 +572,19 @@ public class PgpDecryptVerify {
throw new InvalidDataException();
}
+ WrappedPublicKeyRing signingRing = null;
+ WrappedPublicKey signingKey = null;
+ int signatureIndex = -1;
+
// go through all signatures
// and find out for which signature we have a key in our database
- Long masterKeyId = null;
- String primaryUserId = null;
- int signatureIndex = 0;
for (int i = 0; i < sigList.size(); ++i) {
try {
- Uri uri = KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(
- Long.toString(sigList.get(i).getKeyID()));
- Map<String, Object> data = mProviderHelper.getGenericData(uri,
- new String[] { KeyRings.MASTER_KEY_ID, KeyRings.USER_ID },
- new int[] { ProviderHelper.FIELD_TYPE_INTEGER,
- ProviderHelper.FIELD_TYPE_STRING }
+ long sigKeyId = sigList.get(i).getKeyID();
+ signingRing = mProviderHelper.getWrappedPublicKeyRing(
+ KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(sigKeyId)
);
- masterKeyId = (Long) data.get(KeyRings.MASTER_KEY_ID);
- primaryUserId = (String) data.get(KeyRings.USER_ID);
+ signingKey = signingRing.getSubkey(sigKeyId);
signatureIndex = i;
} catch (ProviderHelper.NotFoundException e) {
Log.d(Constants.TAG, "key not found!");
@@ -641,44 +593,25 @@ public class PgpDecryptVerify {
}
PGPSignature signature = null;
- PGPPublicKey signatureKey = null;
- if (masterKeyId != null) {
+
+ if (signingKey != null) {
// key found in our database!
signature = sigList.get(signatureIndex);
- PGPPublicKeyRing publicKeyRing = null;
- try {
- publicKeyRing = mProviderHelper
- .getPGPPublicKeyRing(masterKeyId);
- } catch (ProviderHelper.NotFoundException e) {
- // can't happen
- }
-
- // get the subkey which has been used to generate this signature
- signatureKey = publicKeyRing.getPublicKey(signature.getKeyID());
-
signatureResultBuilder.signatureAvailable(true);
signatureResultBuilder.knownKey(true);
- signatureResultBuilder.userId(primaryUserId);
- signatureResultBuilder.keyId(masterKeyId);
+ signatureResultBuilder.keyId(signingRing.getMasterKeyId());
+ try {
+ signatureResultBuilder.userId(signingRing.getPrimaryUserId());
+ } catch(PgpGeneralException e) {
+ Log.d(Constants.TAG, "No primary user id in key " + signingRing.getMasterKeyId());
+ }
+ signatureResultBuilder.signatureKeyCertified(signingRing.getVerified() > 0);
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
new JcaPGPContentVerifierBuilderProvider()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
- signature.init(contentVerifierBuilderProvider, signatureKey);
-
- // get certification status of this key
- boolean isSignatureKeyCertified;
- try {
- Object data = mProviderHelper.getGenericData(
- KeychainContract.KeyRings.buildUnifiedKeyRingUri(Long.toString(masterKeyId)),
- KeyRings.VERIFIED,
- ProviderHelper.FIELD_TYPE_INTEGER);
- isSignatureKeyCertified = ((Long) data > 0);
- } catch (ProviderHelper.NotFoundException e) {
- isSignatureKeyCertified = false;
- }
- signatureResultBuilder.signatureKeyCertified(isSignatureKeyCertified);
+ signature.init(contentVerifierBuilderProvider, signingKey.getPublicKey());
} else {
// no key in our database -> return "unknown pub key" status including the first key id
if (!sigList.isEmpty()) {
@@ -710,7 +643,7 @@ public class PgpDecryptVerify {
// Verify signature and check binding signatures
boolean validSignature = signature.verify();
- boolean validKeyBinding = verifyKeyBinding(signature, signatureKey);
+ boolean validKeyBinding = signingRing.verifySubkeyBinding(signingKey);
signatureResultBuilder.validSignature(validSignature);
signatureResultBuilder.validKeyBinding(validKeyBinding);
@@ -722,113 +655,6 @@ public class PgpDecryptVerify {
return result;
}
- private boolean verifyKeyBinding(PGPSignature signature, PGPPublicKey signatureKey) {
- long signatureKeyId = signature.getKeyID();
- boolean validKeyBinding = false;
-
- PGPPublicKey mKey = null;
- try {
- PGPPublicKeyRing signKeyRing = mProviderHelper.getPGPPublicKeyRingWithKeyId(
- signatureKeyId);
- mKey = signKeyRing.getPublicKey();
- } catch (ProviderHelper.NotFoundException e) {
- Log.d(Constants.TAG, "key not found");
- }
-
- if (signature.getKeyID() != mKey.getKeyID()) {
- validKeyBinding = verifyKeyBinding(mKey, signatureKey);
- } else { //if the key used to make the signature was the master key, no need to check binding sigs
- validKeyBinding = true;
- }
- return validKeyBinding;
- }
-
- private boolean verifyKeyBinding(PGPPublicKey masterPublicKey, PGPPublicKey signingPublicKey) {
- boolean validSubkeyBinding = false;
- boolean validTempSubkeyBinding = false;
- boolean validPrimaryKeyBinding = false;
-
- JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
- new JcaPGPContentVerifierBuilderProvider()
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
-
- Iterator<PGPSignature> itr = signingPublicKey.getSignatures();
-
- 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
- //either, so we will err on bad subkey binding here.
- PGPSignature sig = itr.next();
- if (sig.getKeyID() == masterPublicKey.getKeyID() &&
- sig.getSignatureType() == PGPSignature.SUBKEY_BINDING) {
- //check and if ok, check primary key binding.
- try {
- sig.init(contentVerifierBuilderProvider, masterPublicKey);
- validTempSubkeyBinding = sig.verifyCertification(masterPublicKey, signingPublicKey);
- } catch (PGPException e) {
- continue;
- } catch (SignatureException e) {
- continue;
- }
-
- if (validTempSubkeyBinding) {
- validSubkeyBinding = true;
- }
- if (validTempSubkeyBinding) {
- validPrimaryKeyBinding = verifyPrimaryKeyBinding(sig.getUnhashedSubPackets(),
- masterPublicKey, signingPublicKey);
- if (validPrimaryKeyBinding) {
- break;
- }
- validPrimaryKeyBinding = verifyPrimaryKeyBinding(sig.getHashedSubPackets(),
- masterPublicKey, signingPublicKey);
- if (validPrimaryKeyBinding) {
- break;
- }
- }
- }
- }
- return (validSubkeyBinding & validPrimaryKeyBinding);
- }
-
- private 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)) {
- try {
- eSigList = pkts.getEmbeddedSignatures();
- } catch (IOException e) {
- return false;
- } catch (PGPException e) {
- return false;
- }
- for (int j = 0; j < eSigList.size(); ++j) {
- PGPSignature emSig = eSigList.get(j);
- if (emSig.getSignatureType() == PGPSignature.PRIMARYKEY_BINDING) {
- try {
- emSig.init(contentVerifierBuilderProvider, signingPublicKey);
- validPrimaryKeyBinding = emSig.verifyCertification(masterPublicKey, signingPublicKey);
- if (validPrimaryKeyBinding) {
- break;
- }
- } catch (PGPException e) {
- continue;
- } catch (SignatureException e) {
- continue;
- }
- }
- }
- }
-
- return validPrimaryKeyBinding;
- }
-
/**
* Mostly taken from ClearSignedFileProcessor in Bouncy Castle
*
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 f64282f5f..1e58c188f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java
@@ -24,20 +24,14 @@ import android.os.Environment;
import org.spongycastle.bcpg.ArmoredOutputStream;
import org.spongycastle.openpgp.PGPException;
-import org.spongycastle.openpgp.PGPKeyRing;
-import org.spongycastle.openpgp.PGPPublicKey;
-import org.spongycastle.openpgp.PGPPublicKeyRing;
-import org.spongycastle.openpgp.PGPSecretKey;
-import org.spongycastle.openpgp.PGPSecretKeyRing;
-import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
-import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
-import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.keyimport.Keyserver.AddKeyException;
import org.sufficientlysecure.keychain.util.Log;
@@ -62,7 +56,6 @@ public class PgpImportExport {
private ProviderHelper mProviderHelper;
public static final int RETURN_OK = 0;
- public static final int RETURN_ERROR = -1;
public static final int RETURN_BAD = -2;
public static final int RETURN_UPDATED = 1;
@@ -100,12 +93,12 @@ public class PgpImportExport {
}
}
- public boolean uploadKeyRingToServer(HkpKeyserver server, PGPPublicKeyRing keyring) {
+ public boolean uploadKeyRingToServer(HkpKeyserver server, WrappedPublicKeyRing keyring) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ArmoredOutputStream aos = null;
try {
aos = new ArmoredOutputStream(bos);
- aos.write(keyring.getEncoded());
+ keyring.encode(aos);
aos.close();
String armoredKey = bos.toString("UTF-8");
@@ -133,7 +126,7 @@ public class PgpImportExport {
/**
* Imports keys from given data. If keyIds is given only those are imported
*/
- public Bundle importKeyRings(List<ImportKeysListEntry> entries)
+ public Bundle importKeyRings(List<ParcelableKeyRing> entries)
throws PgpGeneralException, PGPException, IOException {
Bundle returnData = new Bundle();
@@ -144,37 +137,26 @@ public class PgpImportExport {
int badKeys = 0;
int position = 0;
- try {
- for (ImportKeysListEntry entry : entries) {
- Object obj = PgpConversionHelper.BytesToPGPKeyRing(entry.getBytes());
-
- if (obj instanceof PGPKeyRing) {
- PGPKeyRing keyring = (PGPKeyRing) obj;
-
- int status = storeKeyRingInCache(keyring);
-
- if (status == RETURN_ERROR) {
- throw new PgpGeneralException(
- mContext.getString(R.string.error_saving_keys));
- }
-
- // update the counts to display to the user at the end
- if (status == RETURN_UPDATED) {
- ++oldKeys;
- } else if (status == RETURN_OK) {
- ++newKeys;
- } else if (status == RETURN_BAD) {
- ++badKeys;
- }
- } else {
- Log.e(Constants.TAG, "Object not recognized as PGPKeyRing!");
- }
-
- position++;
- updateProgress(position / entries.size() * 100, 100);
+ for (ParcelableKeyRing entry : entries) {
+ try {
+ UncachedKeyRing key = entry.getUncachedKeyRing();
+
+ mProviderHelper.savePublicKeyRing(key);
+ /*switch(status) {
+ case RETURN_UPDATED: oldKeys++; break;
+ case RETURN_OK: newKeys++; break;
+ case RETURN_BAD: badKeys++; break;
+ }*/
+ // TODO proper import feedback
+ newKeys += 1;
+
+ } catch (PgpGeneralException e) {
+ Log.e(Constants.TAG, "Encountered bad key on import!", e);
+ ++badKeys;
}
- } catch (Exception e) {
- Log.e(Constants.TAG, "Exception on parsing key file!", e);
+ // update progress
+ position++;
+ updateProgress(position / entries.size() * 100, 100);
}
returnData.putInt(KeychainIntentService.RESULT_IMPORT_ADDED, newKeys);
@@ -211,9 +193,11 @@ public class PgpImportExport {
updateProgress(progress * 100 / masterKeyIdsSize, 100);
try {
- PGPPublicKeyRing publicKeyRing = mProviderHelper.getPGPPublicKeyRing(pubKeyMasterId);
+ WrappedPublicKeyRing ring = mProviderHelper.getWrappedPublicKeyRing(
+ KeychainContract.KeyRings.buildGenericKeyRingUri(pubKeyMasterId)
+ );
- publicKeyRing.encode(arOutStream);
+ ring.encode(arOutStream);
} catch (ProviderHelper.NotFoundException e) {
Log.e(Constants.TAG, "key not found!", e);
// TODO: inform user?
@@ -237,7 +221,8 @@ public class PgpImportExport {
updateProgress(progress * 100 / masterKeyIdsSize, 100);
try {
- PGPSecretKeyRing secretKeyRing = mProviderHelper.getPGPSecretKeyRing(secretKeyMasterId);
+ WrappedSecretKeyRing secretKeyRing =
+ mProviderHelper.getWrappedSecretKeyRing(secretKeyMasterId);
secretKeyRing.encode(arOutStream);
} catch (ProviderHelper.NotFoundException e) {
Log.e(Constants.TAG, "key not found!", e);
@@ -259,53 +244,4 @@ public class PgpImportExport {
return returnData;
}
- @SuppressWarnings("unchecked")
- public int storeKeyRingInCache(PGPKeyRing keyring) {
- int status = RETURN_ERROR;
- try {
- if (keyring instanceof PGPSecretKeyRing) {
- PGPSecretKeyRing secretKeyRing = (PGPSecretKeyRing) keyring;
- boolean save = true;
-
- for (PGPSecretKey testSecretKey : new IterableIterator<PGPSecretKey>(
- secretKeyRing.getSecretKeys())) {
- if (!testSecretKey.isMasterKey()) {
- if (testSecretKey.isPrivateKeyEmpty()) {
- // this is bad, something is very wrong...
- save = false;
- status = RETURN_BAD;
- }
- }
- }
-
- if (save) {
- // TODO: preserve certifications
- // (http://osdir.com/ml/encryption.bouncy-castle.devel/2007-01/msg00054.html ?)
- PGPPublicKeyRing newPubRing = null;
- for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(
- secretKeyRing.getPublicKeys())) {
- if (newPubRing == null) {
- newPubRing = new PGPPublicKeyRing(key.getEncoded(),
- new JcaKeyFingerprintCalculator());
- }
- newPubRing = PGPPublicKeyRing.insertPublicKey(newPubRing, key);
- }
- if (newPubRing != null) {
- mProviderHelper.saveKeyRing(newPubRing);
- }
- mProviderHelper.saveKeyRing(secretKeyRing);
- status = RETURN_OK;
- }
- } else if (keyring instanceof PGPPublicKeyRing) {
- PGPPublicKeyRing publicKeyRing = (PGPPublicKeyRing) keyring;
- mProviderHelper.saveKeyRing(publicKeyRing);
- status = RETURN_OK;
- }
- } catch (IOException e) {
- status = RETURN_ERROR;
- }
-
- return status;
- }
-
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java
index f90250f57..b25c38f1a 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java
@@ -52,14 +52,12 @@ public class PgpKeyHelper {
private static final Pattern USER_ID_PATTERN = Pattern.compile("^(.*?)(?: \\((.*)\\))?(?: <(.*)>)?$");
+ @Deprecated
public static Date getCreationDate(PGPPublicKey key) {
return key.getCreationTime();
}
- public static Date getCreationDate(PGPSecretKey key) {
- return key.getPublicKey().getCreationTime();
- }
-
+ @Deprecated
public static Date getExpiryDate(PGPPublicKey key) {
Date creationDate = getCreationDate(key);
if (key.getValidDays() == 0) {
@@ -73,185 +71,6 @@ public class PgpKeyHelper {
return calendar.getTime();
}
- public static Date getExpiryDate(PGPSecretKey key) {
- return getExpiryDate(key.getPublicKey());
- }
-
- public static boolean isExpired(PGPPublicKey key) {
- Date creationDate = getCreationDate(key);
- Date expiryDate = getExpiryDate(key);
- Date now = new Date();
- if (now.compareTo(creationDate) >= 0
- && (expiryDate == null || now.compareTo(expiryDate) <= 0)) {
- return false;
- }
- return true;
- }
-
- @SuppressWarnings("unchecked")
- public static PGPSecretKey getKeyNum(PGPSecretKeyRing keyRing, long num) {
- long cnt = 0;
- if (keyRing == null) {
- return null;
- }
- for (PGPSecretKey key : new IterableIterator<PGPSecretKey>(keyRing.getSecretKeys())) {
- if (cnt == num) {
- return key;
- }
- cnt++;
- }
-
- return null;
- }
-
- @SuppressWarnings("unchecked")
- private static Vector<PGPPublicKey> getEncryptKeys(PGPPublicKeyRing keyRing) {
- Vector<PGPPublicKey> encryptKeys = new Vector<PGPPublicKey>();
-
- for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(keyRing.getPublicKeys())) {
- if (isEncryptionKey(key)) {
- encryptKeys.add(key);
- }
- }
-
- return encryptKeys;
- }
-
- @SuppressWarnings("unchecked")
- private static Vector<PGPSecretKey> getSigningKeys(PGPSecretKeyRing keyRing) {
- Vector<PGPSecretKey> signingKeys = new Vector<PGPSecretKey>();
-
- for (PGPSecretKey key : new IterableIterator<PGPSecretKey>(keyRing.getSecretKeys())) {
- if (isSigningKey(key)) {
- signingKeys.add(key);
- }
- }
-
- return signingKeys;
- }
-
- @SuppressWarnings("unchecked")
- private static Vector<PGPSecretKey> getCertificationKeys(PGPSecretKeyRing keyRing) {
- Vector<PGPSecretKey> signingKeys = new Vector<PGPSecretKey>();
-
- for (PGPSecretKey key : new IterableIterator<PGPSecretKey>(keyRing.getSecretKeys())) {
- if (isCertificationKey(key)) {
- signingKeys.add(key);
- }
- }
-
- return signingKeys;
- }
-
- private static Vector<PGPPublicKey> getUsableEncryptKeys(PGPPublicKeyRing keyRing) {
- Vector<PGPPublicKey> usableKeys = new Vector<PGPPublicKey>();
- Vector<PGPPublicKey> encryptKeys = getEncryptKeys(keyRing);
- PGPPublicKey masterKey = null;
- for (int i = 0; i < encryptKeys.size(); ++i) {
- PGPPublicKey key = encryptKeys.get(i);
- if (!isExpired(key) && !key.isRevoked()) {
- if (key.isMasterKey()) {
- masterKey = key;
- } else {
- usableKeys.add(key);
- }
- }
- }
- if (masterKey != null) {
- usableKeys.add(masterKey);
- }
- return usableKeys;
- }
-
- private static Vector<PGPSecretKey> getUsableCertificationKeys(PGPSecretKeyRing keyRing) {
- Vector<PGPSecretKey> usableKeys = new Vector<PGPSecretKey>();
- Vector<PGPSecretKey> signingKeys = getCertificationKeys(keyRing);
- PGPSecretKey masterKey = null;
- for (int i = 0; i < signingKeys.size(); ++i) {
- PGPSecretKey key = signingKeys.get(i);
- if (key.isMasterKey()) {
- masterKey = key;
- } else {
- usableKeys.add(key);
- }
- }
- if (masterKey != null) {
- usableKeys.add(masterKey);
- }
- return usableKeys;
- }
-
- private static Vector<PGPSecretKey> getUsableSigningKeys(PGPSecretKeyRing keyRing) {
- Vector<PGPSecretKey> usableKeys = new Vector<PGPSecretKey>();
- Vector<PGPSecretKey> signingKeys = getSigningKeys(keyRing);
- PGPSecretKey masterKey = null;
- for (int i = 0; i < signingKeys.size(); ++i) {
- PGPSecretKey key = signingKeys.get(i);
- if (key.isMasterKey()) {
- masterKey = key;
- } else {
- usableKeys.add(key);
- }
- }
- if (masterKey != null) {
- usableKeys.add(masterKey);
- }
- return usableKeys;
- }
-
-
- public static PGPPublicKey getFirstEncryptSubkey(PGPPublicKeyRing keyRing) {
- Vector<PGPPublicKey> encryptKeys = getUsableEncryptKeys(keyRing);
- if (encryptKeys.size() == 0) {
- Log.e(Constants.TAG, "encryptKeys is null!");
- return null;
- }
- return encryptKeys.get(0);
- }
-
- public static PGPSecretKey getFirstCertificationSubkey(PGPSecretKeyRing keyRing) {
- Vector<PGPSecretKey> signingKeys = getUsableCertificationKeys(keyRing);
- if (signingKeys.size() == 0) {
- return null;
- }
- return signingKeys.get(0);
- }
-
- public static PGPSecretKey getFirstSigningSubkey(PGPSecretKeyRing keyRing) {
- Vector<PGPSecretKey> signingKeys = getUsableSigningKeys(keyRing);
- if (signingKeys.size() == 0) {
- return null;
- }
- return signingKeys.get(0);
- }
-
- public static int getKeyUsage(PGPSecretKey key) {
- return getKeyUsage(key.getPublicKey());
- }
-
- @SuppressWarnings("unchecked")
- private static int getKeyUsage(PGPPublicKey key) {
- int usage = 0;
- if (key.getVersion() >= 4) {
- for (PGPSignature sig : new IterableIterator<PGPSignature>(key.getSignatures())) {
- if (key.isMasterKey() && sig.getKeyID() != key.getKeyID()) {
- continue;
- }
-
- PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets();
- if (hashed != null) {
- usage |= hashed.getKeyFlags();
- }
-
- PGPSignatureSubpacketVector unhashed = sig.getUnhashedSubPackets();
- if (unhashed != null) {
- usage |= unhashed.getKeyFlags();
- }
- }
- }
- return usage;
- }
-
@SuppressWarnings("unchecked")
public static boolean isEncryptionKey(PGPPublicKey key) {
if (!key.isEncryptionKey()) {
@@ -293,10 +112,6 @@ public class PgpKeyHelper {
return false;
}
- public static boolean isEncryptionKey(PGPSecretKey key) {
- return isEncryptionKey(key.getPublicKey());
- }
-
@SuppressWarnings("unchecked")
public static boolean isSigningKey(PGPPublicKey key) {
if (key.getVersion() <= 3) {
@@ -328,10 +143,6 @@ public class PgpKeyHelper {
return false;
}
- public static boolean isSigningKey(PGPSecretKey key) {
- return isSigningKey(key.getPublicKey());
- }
-
@SuppressWarnings("unchecked")
public static boolean isCertificationKey(PGPPublicKey key) {
if (key.getVersion() <= 3) {
@@ -358,48 +169,6 @@ public class PgpKeyHelper {
return false;
}
- public static boolean isAuthenticationKey(PGPSecretKey key) {
- return isAuthenticationKey(key.getPublicKey());
- }
-
- @SuppressWarnings("unchecked")
- public static boolean isAuthenticationKey(PGPPublicKey key) {
- if (key.getVersion() <= 3) {
- return true;
- }
-
- for (PGPSignature sig : new IterableIterator<PGPSignature>(key.getSignatures())) {
- if (key.isMasterKey() && sig.getKeyID() != key.getKeyID()) {
- continue;
- }
- PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets();
-
- if (hashed != null && (hashed.getKeyFlags() & KeyFlags.AUTHENTICATION) != 0) {
- return true;
- }
-
- PGPSignatureSubpacketVector unhashed = sig.getUnhashedSubPackets();
-
- if (unhashed != null && (unhashed.getKeyFlags() & KeyFlags.AUTHENTICATION) != 0) {
- return true;
- }
- }
-
- return false;
- }
-
- public static boolean isCertificationKey(PGPSecretKey key) {
- return isCertificationKey(key.getPublicKey());
- }
-
- public static String getAlgorithmInfo(Context context, PGPPublicKey key) {
- return getAlgorithmInfo(context, key.getAlgorithm(), key.getBitStrength());
- }
-
- public static String getAlgorithmInfo(Context context, PGPSecretKey key) {
- return getAlgorithmInfo(context, key.getPublicKey());
- }
-
/**
* TODO: Only used in HkpKeyServer. Get rid of this one!
*/
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
index 9dd9f660b..44fc4c8c9 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
@@ -48,8 +48,8 @@ import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.pgp.Progressable;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException;
+import org.sufficientlysecure.keychain.service.OldSaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Primes;
@@ -63,6 +63,7 @@ import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.SignatureException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
@@ -124,7 +125,7 @@ public class PgpKeyOperation {
*/
// TODO: key flags?
- public PGPSecretKey createKey(int algorithmChoice, int keySize, String passphrase,
+ public byte[] createKey(int algorithmChoice, int keySize, String passphrase,
boolean isMasterKey)
throws NoSuchAlgorithmException, PGPException, NoSuchProviderException,
PgpGeneralMsgIdException, InvalidAlgorithmParameterException {
@@ -188,43 +189,23 @@ public class PgpKeyOperation {
PGPEncryptedData.CAST5, sha1Calc)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
- return new PGPSecretKey(keyPair.getPrivateKey(), keyPair.getPublicKey(),
- sha1Calc, isMasterKey, keyEncryptor);
- }
-
- public PGPSecretKeyRing changeSecretKeyPassphrase(PGPSecretKeyRing keyRing, String oldPassphrase,
- String newPassphrase)
- throws IOException, PGPException, NoSuchProviderException {
-
- updateProgress(R.string.progress_building_key, 0, 100);
- if (oldPassphrase == null) {
- oldPassphrase = "";
- }
- if (newPassphrase == null) {
- newPassphrase = "";
+ try {
+ return new PGPSecretKey(keyPair.getPrivateKey(), keyPair.getPublicKey(),
+ sha1Calc, isMasterKey, keyEncryptor).getEncoded();
+ } catch(IOException e) {
+ throw new PgpGeneralMsgIdException(R.string.error_encoding);
}
-
- PGPSecretKeyRing newKeyRing = PGPSecretKeyRing.copyWithNewPassword(
- keyRing,
- new JcePBESecretKeyDecryptorBuilder(new JcaPGPDigestCalculatorProviderBuilder()
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build()).setProvider(
- Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(oldPassphrase.toCharArray()),
- new JcePBESecretKeyEncryptorBuilder(keyRing.getSecretKey()
- .getKeyEncryptionAlgorithm()).build(newPassphrase.toCharArray()));
-
- return newKeyRing;
-
}
- public Pair<PGPSecretKeyRing, PGPPublicKeyRing> buildNewSecretKey(
- SaveKeyringParcel saveParcel)
+ public Pair<UncachedKeyRing,UncachedKeyRing> buildNewSecretKey(
+ OldSaveKeyringParcel saveParcel)
throws PgpGeneralMsgIdException, PGPException, SignatureException, IOException {
int usageId = saveParcel.keysUsages.get(0);
boolean canSign;
String mainUserId = saveParcel.userIds.get(0);
- PGPSecretKey masterKey = saveParcel.keys.get(0);
+ PGPSecretKey masterKey = saveParcel.keys.get(0).getSecretKeyExternal();
// this removes all userIds and certifications previously attached to the masterPublicKey
PGPPublicKey masterPublicKey = masterKey.getPublicKey();
@@ -299,7 +280,7 @@ public class PgpKeyOperation {
for (int i = 1; i < saveParcel.keys.size(); ++i) {
updateProgress(40 + 40 * (i - 1) / (saveParcel.keys.size() - 1), 100);
- PGPSecretKey subKey = saveParcel.keys.get(i);
+ PGPSecretKey subKey = saveParcel.keys.get(i).getSecretKeyExternal();
PGPPublicKey subPublicKey = subKey.getPublicKey();
PBESecretKeyDecryptor keyDecryptor2 = new JcePBESecretKeyDecryptorBuilder()
@@ -357,17 +338,19 @@ public class PgpKeyOperation {
PGPSecretKeyRing secretKeyRing = keyGen.generateSecretKeyRing();
PGPPublicKeyRing publicKeyRing = keyGen.generatePublicKeyRing();
- return new Pair<PGPSecretKeyRing, PGPPublicKeyRing>(secretKeyRing, publicKeyRing);
+ return new Pair(new UncachedKeyRing(secretKeyRing), new UncachedKeyRing(publicKeyRing));
}
- public Pair<PGPSecretKeyRing, PGPPublicKeyRing> buildSecretKey(PGPSecretKeyRing mKR,
- PGPPublicKeyRing pKR,
- SaveKeyringParcel saveParcel)
+ public Pair<UncachedKeyRing, UncachedKeyRing> buildSecretKey(WrappedSecretKeyRing wmKR,
+ WrappedPublicKeyRing wpKR,
+ OldSaveKeyringParcel saveParcel)
throws PgpGeneralMsgIdException, PGPException, SignatureException, IOException {
+ PGPSecretKeyRing mKR = wmKR.getRing();
+ PGPPublicKeyRing pKR = wpKR.getRing();
+
updateProgress(R.string.progress_building_key, 0, 100);
- PGPSecretKey masterKey = saveParcel.keys.get(0);
if (saveParcel.oldPassphrase == null) {
saveParcel.oldPassphrase = "";
@@ -399,12 +382,12 @@ public class PgpKeyOperation {
*/
if (saveParcel.deletedKeys != null) {
- for (PGPSecretKey dKey : saveParcel.deletedKeys) {
- mKR = PGPSecretKeyRing.removeSecretKey(mKR, dKey);
+ for (UncachedSecretKey dKey : saveParcel.deletedKeys) {
+ mKR = PGPSecretKeyRing.removeSecretKey(mKR, dKey.getSecretKeyExternal());
}
}
- masterKey = mKR.getSecretKey();
+ PGPSecretKey masterKey = mKR.getSecretKey();
PGPPublicKey masterPublicKey = masterKey.getPublicKey();
int usageId = saveParcel.keysUsages.get(0);
@@ -564,7 +547,7 @@ public class PgpKeyOperation {
for (int i = 1; i < saveParcel.keys.size(); ++i) {
updateProgress(40 + 50 * i / saveParcel.keys.size(), 100);
if (saveParcel.moddedKeys[i]) {
- PGPSecretKey subKey = saveParcel.keys.get(i);
+ PGPSecretKey subKey = saveParcel.keys.get(i).getSecretKeyExternal();
PGPPublicKey subPublicKey = subKey.getPublicKey();
PBESecretKeyDecryptor keyDecryptor2;
@@ -667,7 +650,7 @@ public class PgpKeyOperation {
for(String uid : new IterableIterator<String>(secretKeyRing.getPublicKey().getUserIDs())) {
for(PGPSignature sig : new IterableIterator<PGPSignature>(
- secretKeyRing.getPublicKey().getSignaturesForID(uid))) {
+ secretKeyRing.getPublicKey().getSignaturesForId(uid))) {
Log.d(Constants.TAG, "sig: " +
PgpKeyHelper.convertKeyIdToHex(sig.getKeyID()) + " for " + uid);
}
@@ -678,7 +661,7 @@ public class PgpKeyOperation {
for(String uid : new IterableIterator<String>(publicKeyRing.getPublicKey().getUserIDs())) {
for(PGPSignature sig : new IterableIterator<PGPSignature>(
- publicKeyRing.getPublicKey().getSignaturesForID(uid))) {
+ publicKeyRing.getPublicKey().getSignaturesForId(uid))) {
Log.d(Constants.TAG, "sig: " +
PgpKeyHelper.convertKeyIdToHex(sig.getKeyID()) + " for " + uid);
}
@@ -686,10 +669,287 @@ public class PgpKeyOperation {
*/
- return new Pair<PGPSecretKeyRing, PGPPublicKeyRing>(mKR, pKR);
+ return new Pair<UncachedKeyRing,UncachedKeyRing>(new UncachedKeyRing(pKR),
+ new UncachedKeyRing(mKR));
}
+ public Pair<PGPSecretKeyRing, PGPPublicKeyRing> buildSecretKey(PGPSecretKeyRing sKR,
+ PGPPublicKeyRing pKR,
+ SaveKeyringParcel saveParcel,
+ String passphrase)
+ throws PgpGeneralMsgIdException, PGPException, SignatureException, IOException {
+
+ updateProgress(R.string.progress_building_key, 0, 100);
+
+ // sort these, so we can use binarySearch later on
+ Arrays.sort(saveParcel.revokeSubKeys);
+ Arrays.sort(saveParcel.revokeUserIds);
+
+ /*
+ * What's gonna happen here:
+ *
+ * 1. Unlock private key
+ *
+ * 2. Create new secret key ring
+ *
+ * 3. Copy subkeys
+ * - Generate revocation if requested
+ * - Copy old cert, or generate new if change requested
+ *
+ * 4. Generate and add new subkeys
+ *
+ * 5. Copy user ids
+ * - Generate revocation if requested
+ * - Copy old cert, or generate new if primary user id status changed
+ *
+ * 6. Add new user ids
+ *
+ * 7. Generate PublicKeyRing from SecretKeyRing
+ *
+ * 8. Return pair (PublicKeyRing,SecretKeyRing)
+ *
+ */
+
+ // 1. Unlock private key
+ updateProgress(R.string.progress_building_key, 0, 100);
+
+ PGPPublicKey masterPublicKey = sKR.getPublicKey();
+ PGPPrivateKey masterPrivateKey; {
+ PGPSecretKey masterKey = sKR.getSecretKey();
+ PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
+ Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
+ masterPrivateKey = masterKey.extractPrivateKey(keyDecryptor);
+ }
+
+ // 2. Create new secret key ring
+ updateProgress(R.string.progress_certifying_master_key, 20, 100);
+
+ // Note we do NOT use PGPKeyRingGeneraor, it's just one level too high and does stuff
+ // we want to do manually. Instead, we simply use a list of secret keys.
+ ArrayList<PGPSecretKey> secretKeys = new ArrayList<PGPSecretKey>();
+ ArrayList<PGPPublicKey> publicKeys = new ArrayList<PGPPublicKey>();
+
+ // 3. Copy subkeys
+ // - Generate revocation if requested
+ // - Copy old cert, or generate new if change requested
+ for (PGPSecretKey sKey : new IterableIterator<PGPSecretKey>(sKR.getSecretKeys())) {
+ PGPPublicKey pKey = sKey.getPublicKey();
+ if (Arrays.binarySearch(saveParcel.revokeSubKeys, sKey.getKeyID()) >= 0) {
+ // add revocation signature to key, if there is none yet
+ if (!pKey.getSignaturesOfType(PGPSignature.SUBKEY_REVOCATION).hasNext()) {
+ // generate revocation signature
+ }
+ }
+ if (saveParcel.changeSubKeys.containsKey(sKey.getKeyID())) {
+ // change subkey flags?
+ SaveKeyringParcel.SubkeyChange change = saveParcel.changeSubKeys.get(sKey.getKeyID());
+ // remove old subkey binding signature(s?)
+ for (PGPSignature sig : new IterableIterator<PGPSignature>(
+ pKey.getSignaturesOfType(PGPSignature.SUBKEY_BINDING))) {
+ pKey = PGPPublicKey.removeCertification(pKey, sig);
+ }
+
+ // generate and add new signature
+ PGPSignature sig = generateSubkeyBindingSignature(masterPublicKey, masterPrivateKey,
+ sKey, pKey, change.mFlags, change.mExpiry, passphrase);
+ pKey = PGPPublicKey.addCertification(pKey, sig);
+ }
+ secretKeys.add(PGPSecretKey.replacePublicKey(sKey, pKey));
+ publicKeys.add(pKey);
+ }
+
+ // 4. Generate and add new subkeys
+ // TODO
+
+ // 5. Copy user ids
+ for (String userId : new IterableIterator<String>(masterPublicKey.getUserIDs())) {
+ // - Copy old cert, or generate new if primary user id status changed
+ boolean certified = false, revoked = false;
+ for (PGPSignature sig : new IterableIterator<PGPSignature>(
+ masterPublicKey.getSignaturesForID(userId))) {
+ // We know there are only revocation and certification types in here.
+ switch(sig.getSignatureType()) {
+ case PGPSignature.CERTIFICATION_REVOCATION:
+ revoked = true;
+ continue;
+
+ case PGPSignature.DEFAULT_CERTIFICATION:
+ case PGPSignature.NO_CERTIFICATION:
+ case PGPSignature.CASUAL_CERTIFICATION:
+ case PGPSignature.POSITIVE_CERTIFICATION:
+ // Already got one? Remove this one, then.
+ if (certified) {
+ masterPublicKey = PGPPublicKey.removeCertification(
+ masterPublicKey, userId, sig);
+ continue;
+ }
+ boolean primary = userId.equals(saveParcel.changePrimaryUserId);
+ // Generate a new one under certain circumstances
+ if (saveParcel.changePrimaryUserId != null &&
+ sig.getHashedSubPackets().isPrimaryUserID() != primary) {
+ PGPSignature cert = generateUserIdSignature(
+ masterPrivateKey, masterPublicKey, userId, primary);
+ PGPPublicKey.addCertification(masterPublicKey, userId, cert);
+ }
+ certified = true;
+ }
+ }
+ // - Generate revocation if requested
+ if (!revoked && Arrays.binarySearch(saveParcel.revokeUserIds, userId) >= 0) {
+ PGPSignature cert = generateRevocationSignature(masterPrivateKey,
+ masterPublicKey, userId);
+ masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, cert);
+ }
+ }
+
+ // 6. Add new user ids
+ for(String userId : saveParcel.addUserIds) {
+ PGPSignature cert = generateUserIdSignature(masterPrivateKey,
+ masterPublicKey, userId, userId.equals(saveParcel.changePrimaryUserId));
+ masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, cert);
+ }
+
+ // 7. Generate PublicKeyRing from SecretKeyRing
+ updateProgress(R.string.progress_building_master_key, 30, 100);
+ PGPSecretKeyRing ring = new PGPSecretKeyRing(secretKeys);
+
+ // Copy all non-self uid certificates
+ for (String userId : new IterableIterator<String>(masterPublicKey.getUserIDs())) {
+ // - Copy old cert, or generate new if primary user id status changed
+ boolean certified = false, revoked = false;
+ for (PGPSignature sig : new IterableIterator<PGPSignature>(
+ masterPublicKey.getSignaturesForID(userId))) {
+ }
+ }
+
+ for (PGPPublicKey newKey : publicKeys) {
+ PGPPublicKey oldKey = pKR.getPublicKey(newKey.getKeyID());
+ for (PGPSignature sig : new IterableIterator<PGPSignature>(
+ oldKey.getSignatures())) {
+ }
+ }
+
+ // If requested, set new passphrase
+ if (saveParcel.newPassphrase != null) {
+ PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build()
+ .get(HashAlgorithmTags.SHA1);
+ PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
+ Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
+ // Build key encryptor based on new passphrase
+ PBESecretKeyEncryptor keyEncryptorNew = new JcePBESecretKeyEncryptorBuilder(
+ PGPEncryptedData.CAST5, sha1Calc)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
+ saveParcel.newPassphrase.toCharArray());
+
+ sKR = PGPSecretKeyRing.copyWithNewPassword(sKR, keyDecryptor, keyEncryptorNew);
+ }
+
+ // 8. Return pair (PublicKeyRing,SecretKeyRing)
+
+ return new Pair<PGPSecretKeyRing, PGPPublicKeyRing>(sKR, pKR);
+
+ }
+
+ private static PGPSignature generateUserIdSignature(
+ PGPPrivateKey masterPrivateKey, PGPPublicKey pKey, String userId, boolean primary)
+ throws IOException, PGPException, SignatureException {
+ PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
+ pKey.getAlgorithm(), PGPUtil.SHA1)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
+ PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
+ subHashedPacketsGen.setSignatureCreationTime(false, new Date());
+ subHashedPacketsGen.setPreferredSymmetricAlgorithms(true, PREFERRED_SYMMETRIC_ALGORITHMS);
+ subHashedPacketsGen.setPreferredHashAlgorithms(true, PREFERRED_HASH_ALGORITHMS);
+ subHashedPacketsGen.setPreferredCompressionAlgorithms(true, PREFERRED_COMPRESSION_ALGORITHMS);
+ subHashedPacketsGen.setPrimaryUserID(false, primary);
+ sGen.setHashedSubpackets(subHashedPacketsGen.generate());
+ sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey);
+ return sGen.generateCertification(userId, pKey);
+ }
+
+ private static PGPSignature generateRevocationSignature(
+ PGPPrivateKey masterPrivateKey, PGPPublicKey pKey, String userId)
+ throws IOException, PGPException, SignatureException {
+ PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
+ pKey.getAlgorithm(), PGPUtil.SHA1)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
+ PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
+ subHashedPacketsGen.setSignatureCreationTime(false, new Date());
+ sGen.setHashedSubpackets(subHashedPacketsGen.generate());
+ sGen.init(PGPSignature.CERTIFICATION_REVOCATION, masterPrivateKey);
+ return sGen.generateCertification(userId, pKey);
+ }
+
+ private static PGPSignature generateSubkeyBindingSignature(
+ PGPPublicKey masterPublicKey, PGPPrivateKey masterPrivateKey,
+ PGPSecretKey sKey, PGPPublicKey pKey,
+ int flags, Long expiry, String passphrase)
+ throws PgpGeneralMsgIdException, IOException, PGPException, SignatureException {
+
+ // date for signing
+ Date todayDate = new Date();
+ PGPSignatureSubpacketGenerator unhashedPacketsGen = new PGPSignatureSubpacketGenerator();
+
+ // If this key can sign, we need a primary key binding signature
+ if ((flags & KeyFlags.SIGN_DATA) != 0) {
+
+ PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
+ passphrase.toCharArray());
+ PGPPrivateKey subPrivateKey = sKey.extractPrivateKey(keyDecryptor);
+
+ // cross-certify signing keys
+ PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
+ subHashedPacketsGen.setSignatureCreationTime(false, todayDate);
+ PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
+ pKey.getAlgorithm(), PGPUtil.SHA1)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
+ sGen.init(PGPSignature.PRIMARYKEY_BINDING, subPrivateKey);
+ sGen.setHashedSubpackets(subHashedPacketsGen.generate());
+ PGPSignature certification = sGen.generateCertification(masterPublicKey, pKey);
+ unhashedPacketsGen.setEmbeddedSignature(false, certification);
+ }
+
+ PGPSignatureSubpacketGenerator hashedPacketsGen;
+ {
+ hashedPacketsGen = new PGPSignatureSubpacketGenerator();
+ hashedPacketsGen.setSignatureCreationTime(false, todayDate);
+ hashedPacketsGen.setKeyFlags(false, flags);
+ }
+
+ if (expiry != null) {
+ Calendar creationDate = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
+ creationDate.setTime(pKey.getCreationTime());
+ // 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 = (expiry / 86400000) -
+ (creationDate.getTimeInMillis() / 86400000);
+ if (numDays <= 0) {
+ throw new PgpGeneralMsgIdException(R.string.error_expiry_must_come_after_creation);
+ }
+ hashedPacketsGen.setKeyExpirationTime(false, expiry - creationDate.getTimeInMillis());
+ } else {
+ hashedPacketsGen.setKeyExpirationTime(false, 0);
+ }
+
+ PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
+ pKey.getAlgorithm(), PGPUtil.SHA1)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
+ sGen.init(PGPSignature.SUBKEY_BINDING, masterPrivateKey);
+ sGen.setHashedSubpackets(hashedPacketsGen.generate());
+ sGen.setUnhashedSubpackets(unhashedPacketsGen.generate());
+
+ return sGen.generateCertification(masterPublicKey, pKey);
+
+ }
+
+
/**
* Certify the given pubkeyid with the given masterkeyid.
*
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 665dc82cc..4cb92c368 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java
@@ -25,25 +25,14 @@ import org.spongycastle.openpgp.PGPEncryptedDataGenerator;
import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPLiteralData;
import org.spongycastle.openpgp.PGPLiteralDataGenerator;
-import org.spongycastle.openpgp.PGPPrivateKey;
-import org.spongycastle.openpgp.PGPPublicKey;
-import org.spongycastle.openpgp.PGPPublicKeyRing;
-import org.spongycastle.openpgp.PGPSecretKey;
-import org.spongycastle.openpgp.PGPSecretKeyRing;
-import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureGenerator;
-import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator;
import org.spongycastle.openpgp.PGPV3SignatureGenerator;
-import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
-import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePBEKeyEncryptionMethodGenerator;
-import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder;
-import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.pgp.Progressable;
-import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+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;
@@ -277,20 +266,17 @@ public class PgpSignEncrypt {
}
/* Get keys for signature generation for later usage */
- PGPSecretKey signingKey = null;
- PGPSecretKeyRing signingKeyRing = null;
- PGPPrivateKey signaturePrivateKey = null;
- String signingUserId = null;
+ WrappedSecretKey signingKey = null;
if (enableSignature) {
+ WrappedSecretKeyRing signingKeyRing;
try {
- signingKeyRing = mProviderHelper.getPGPSecretKeyRing(mSignatureMasterKeyId);
- signingUserId = (String) mProviderHelper.getUnifiedData(mSignatureMasterKeyId,
- KeychainContract.KeyRings.USER_ID, ProviderHelper.FIELD_TYPE_STRING);
+ signingKeyRing = mProviderHelper.getWrappedSecretKeyRing(mSignatureMasterKeyId);
} catch (ProviderHelper.NotFoundException e) {
throw new NoSigningKeyException();
}
- signingKey = PgpKeyHelper.getFirstSigningSubkey(signingKeyRing);
- if (signingKey == null) {
+ try {
+ signingKey = signingKeyRing.getSigningSubKey();
+ } catch(PgpGeneralException e) {
throw new NoSigningKeyException();
}
@@ -300,10 +286,9 @@ public class PgpSignEncrypt {
updateProgress(R.string.progress_extracting_signature_key, 0, 100);
- PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
- Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(mSignaturePassphrase.toCharArray());
- signaturePrivateKey = signingKey.extractPrivateKey(keyDecryptor);
- if (signaturePrivateKey == null) {
+ try {
+ signingKey.unlock(mSignaturePassphrase);
+ } catch (PgpGeneralException e) {
throw new KeyExtractionException();
}
}
@@ -331,13 +316,12 @@ public class PgpSignEncrypt {
// Asymmetric encryption
for (long id : mEncryptionMasterKeyIds) {
try {
- PGPPublicKeyRing keyRing = mProviderHelper.getPGPPublicKeyRing(id);
- PGPPublicKey key = PgpKeyHelper.getFirstEncryptSubkey(keyRing);
- if (key != null) {
- JcePublicKeyKeyEncryptionMethodGenerator pubKeyEncryptionGenerator =
- new JcePublicKeyKeyEncryptionMethodGenerator(key);
- cPk.addMethod(pubKeyEncryptionGenerator);
- }
+ WrappedPublicKeyRing keyRing = mProviderHelper.getWrappedPublicKeyRing(
+ KeyRings.buildUnifiedKeyRingUri(id));
+ WrappedPublicKey key = keyRing.getEncryptionSubKey();
+ cPk.addMethod(key.getPubKeyEncryptionGenerator());
+ } catch (PgpGeneralException e) {
+ Log.e(Constants.TAG, "key not found!", e);
} catch (ProviderHelper.NotFoundException e) {
Log.e(Constants.TAG, "key not found!", e);
}
@@ -351,29 +335,18 @@ public class PgpSignEncrypt {
if (enableSignature) {
updateProgress(R.string.progress_preparing_signature, 10, 100);
- // content signer based on signing key algorithm and chosen hash algorithm
- JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(
- signingKey.getPublicKey().getAlgorithm(), mSignatureHashAlgorithm)
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
-
- int signatureType;
- if (mCleartextInput && mEnableAsciiArmorOutput && !enableEncryption) {
- // for sign-only ascii text
- signatureType = PGPSignature.CANONICAL_TEXT_DOCUMENT;
- } else {
- signatureType = PGPSignature.BINARY_DOCUMENT;
- }
-
- if (mSignatureForceV3) {
- signatureV3Generator = new PGPV3SignatureGenerator(contentSignerBuilder);
- signatureV3Generator.init(signatureType, signaturePrivateKey);
- } else {
- signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
- signatureGenerator.init(signatureType, signaturePrivateKey);
-
- PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
- spGen.setSignerUserID(false, signingUserId);
- signatureGenerator.setHashedSubpackets(spGen.generate());
+ try {
+ boolean cleartext = mCleartextInput && mEnableAsciiArmorOutput && !enableEncryption;
+ if (mSignatureForceV3) {
+ signatureV3Generator = signingKey.getV3SignatureGenerator(
+ mSignatureHashAlgorithm,cleartext);
+ } else {
+ signatureGenerator = signingKey.getSignatureGenerator(
+ mSignatureHashAlgorithm, cleartext);
+ }
+ } catch (PgpGeneralException e) {
+ // TODO throw correct type of exception (which shouldn't be PGPException)
+ throw new KeyExtractionException();
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
new file mode 100644
index 000000000..02e5411ca
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
@@ -0,0 +1,171 @@
+package org.sufficientlysecure.keychain.pgp;
+
+import org.spongycastle.bcpg.ArmoredOutputStream;
+import org.spongycastle.bcpg.S2K;
+import org.spongycastle.openpgp.PGPKeyRing;
+import org.spongycastle.openpgp.PGPObjectFactory;
+import org.spongycastle.openpgp.PGPPublicKey;
+import org.spongycastle.openpgp.PGPSecretKey;
+import org.spongycastle.openpgp.PGPSecretKeyRing;
+import org.spongycastle.openpgp.PGPUtil;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.util.IterableIterator;
+import org.sufficientlysecure.keychain.util.Log;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Vector;
+
+/** Wrapper around PGPKeyRing class, to be constructed from bytes.
+ *
+ * This class and its relatives UncachedPublicKey and UncachedSecretKey are
+ * used to move around pgp key rings in non crypto related (UI, mostly) code.
+ * It should be used for simple inspection only until it saved in the database,
+ * all actual crypto operations should work with WrappedKeyRings exclusively.
+ *
+ * This class is also special in that it can hold either the PGPPublicKeyRing
+ * or PGPSecretKeyRing derivate of the PGPKeyRing class, since these are
+ * treated equally for most purposes in UI code. It is up to the programmer to
+ * take care of the differences.
+ *
+ * @see org.sufficientlysecure.keychain.pgp.WrappedKeyRing
+ * @see org.sufficientlysecure.keychain.pgp.UncachedPublicKey
+ * @see org.sufficientlysecure.keychain.pgp.UncachedSecretKey
+ *
+ */
+public class UncachedKeyRing {
+
+ final PGPKeyRing mRing;
+ final boolean mIsSecret;
+
+ UncachedKeyRing(PGPKeyRing ring) {
+ mRing = ring;
+ mIsSecret = ring instanceof PGPSecretKeyRing;
+ }
+
+ public long getMasterKeyId() {
+ return mRing.getPublicKey().getKeyID();
+ }
+
+ /* TODO don't use this */
+ @Deprecated
+ public PGPKeyRing getRing() {
+ return mRing;
+ }
+
+ public UncachedPublicKey getPublicKey() {
+ return new UncachedPublicKey(mRing.getPublicKey());
+ }
+
+ public Iterator<UncachedPublicKey> getPublicKeys() {
+ final Iterator<PGPPublicKey> it = mRing.getPublicKeys();
+ return new Iterator<UncachedPublicKey>() {
+ public void remove() {
+ it.remove();
+ }
+ public UncachedPublicKey next() {
+ return new UncachedPublicKey(it.next());
+ }
+ public boolean hasNext() {
+ return it.hasNext();
+ }
+ };
+ }
+
+ /** Returns the dynamic (though final) property if this is a secret keyring or not. */
+ public boolean isSecret() {
+ return mIsSecret;
+ }
+
+ public byte[] getEncoded() throws IOException {
+ return mRing.getEncoded();
+ }
+
+ public byte[] getFingerprint() {
+ return mRing.getPublicKey().getFingerprint();
+ }
+
+ public static UncachedKeyRing decodePublicFromData(byte[] data)
+ throws PgpGeneralException, IOException {
+ UncachedKeyRing ring = decodeFromData(data);
+ if(ring.isSecret()) {
+ throw new PgpGeneralException("Object not recognized as PGPPublicKeyRing!");
+ }
+ return ring;
+ }
+
+ public static UncachedKeyRing decodeFromData(byte[] data)
+ throws PgpGeneralException, IOException {
+ BufferedInputStream bufferedInput =
+ new BufferedInputStream(new ByteArrayInputStream(data));
+ if (bufferedInput.available() > 0) {
+ InputStream in = PGPUtil.getDecoderStream(bufferedInput);
+ PGPObjectFactory objectFactory = new PGPObjectFactory(in);
+
+ // get first object in block
+ Object obj;
+ if ((obj = objectFactory.nextObject()) != null && obj instanceof PGPKeyRing) {
+ return new UncachedKeyRing((PGPKeyRing) obj);
+ } else {
+ throw new PgpGeneralException("Object not recognized as PGPKeyRing!");
+ }
+ } else {
+ throw new PgpGeneralException("Object not recognized as PGPKeyRing!");
+ }
+ }
+
+ public static List<UncachedKeyRing> fromStream(InputStream stream)
+ throws PgpGeneralException, IOException {
+
+ PGPObjectFactory objectFactory = new PGPObjectFactory(PGPUtil.getDecoderStream(stream));
+
+ List<UncachedKeyRing> result = new Vector<UncachedKeyRing>();
+
+ // go through all objects in this block
+ Object obj;
+ while ((obj = objectFactory.nextObject()) != null) {
+ Log.d(Constants.TAG, "Found class: " + obj.getClass());
+
+ if (obj instanceof PGPKeyRing) {
+ result.add(new UncachedKeyRing((PGPKeyRing) obj));
+ } else {
+ Log.e(Constants.TAG, "Object not recognized as PGPKeyRing!");
+ }
+ }
+ return result;
+ }
+
+ public void encodeArmored(OutputStream out, String version) throws IOException {
+ ArmoredOutputStream aos = new ArmoredOutputStream(out);
+ aos.setHeader("Version", version);
+ aos.write(mRing.getEncoded());
+ aos.close();
+ }
+
+ public ArrayList<Long> getAvailableSubkeys() {
+ if(!isSecret()) {
+ throw new RuntimeException("Tried to find available subkeys from non-secret keys. " +
+ "This is a programming error and should never happen!");
+ }
+
+ ArrayList<Long> result = new ArrayList<Long>();
+ // then, mark exactly the keys we have available
+ for (PGPSecretKey sub : new IterableIterator<PGPSecretKey>(
+ ((PGPSecretKeyRing) mRing).getSecretKeys())) {
+ S2K s2k = sub.getS2K();
+ // Set to 1, except if the encryption type is GNU_DUMMY_S2K
+ if(s2k == null || s2k.getType() != S2K.GNU_DUMMY_S2K) {
+ result.add(sub.getKeyID());
+ }
+ }
+ return result;
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java
new file mode 100644
index 000000000..e3db03bf6
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java
@@ -0,0 +1,197 @@
+package org.sufficientlysecure.keychain.pgp;
+
+import org.spongycastle.bcpg.SignatureSubpacketTags;
+import org.spongycastle.bcpg.sig.KeyFlags;
+import org.spongycastle.openpgp.PGPPublicKey;
+import org.spongycastle.openpgp.PGPSignature;
+import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
+import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.util.IterableIterator;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.Iterator;
+import java.util.List;
+
+public class UncachedPublicKey {
+ protected final PGPPublicKey mPublicKey;
+ private Integer mCacheUsage = null;
+
+ public UncachedPublicKey(PGPPublicKey key) {
+ mPublicKey = key;
+ }
+
+ public long getKeyId() {
+ return mPublicKey.getKeyID();
+ }
+
+ /** The revocation signature is NOT checked here, so this may be false! */
+ public boolean maybeRevoked() {
+ return mPublicKey.isRevoked();
+ }
+
+ public Date getCreationTime() {
+ return mPublicKey.getCreationTime();
+ }
+
+ public Date getExpiryTime() {
+ Date creationDate = getCreationTime();
+ if (mPublicKey.getValidDays() == 0) {
+ // no expiry
+ return null;
+ }
+ Calendar calendar = GregorianCalendar.getInstance();
+ calendar.setTime(creationDate);
+ calendar.add(Calendar.DATE, mPublicKey.getValidDays());
+
+ return calendar.getTime();
+ }
+
+ public boolean isExpired() {
+ Date creationDate = mPublicKey.getCreationTime();
+ Date expiryDate = mPublicKey.getValidSeconds() > 0
+ ? new Date(creationDate.getTime() + mPublicKey.getValidSeconds() * 1000) : null;
+
+ Date now = new Date();
+ return creationDate.after(now) || (expiryDate != null && expiryDate.before(now));
+ }
+
+ public boolean isMasterKey() {
+ return mPublicKey.isMasterKey();
+ }
+
+ public int getAlgorithm() {
+ return mPublicKey.getAlgorithm();
+ }
+
+ public int getBitStrength() {
+ return mPublicKey.getBitStrength();
+ }
+
+ public String getPrimaryUserId() {
+ for (String userId : new IterableIterator<String>(mPublicKey.getUserIDs())) {
+ for (PGPSignature sig : new IterableIterator<PGPSignature>(mPublicKey.getSignaturesForID(userId))) {
+ if (sig.getHashedSubPackets() != null
+ && sig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.PRIMARY_USER_ID)) {
+ try {
+ // make sure it's actually valid
+ sig.init(new JcaPGPContentVerifierBuilderProvider().setProvider(
+ Constants.BOUNCY_CASTLE_PROVIDER_NAME), mPublicKey);
+ if (sig.verifyCertification(userId, mPublicKey)) {
+ return userId;
+ }
+ } catch (Exception e) {
+ // nothing bad happens, the key is just not considered the primary key id
+ }
+ }
+
+ }
+ }
+ return null;
+ }
+
+ public ArrayList<String> getUnorderedUserIds() {
+ ArrayList<String> userIds = new ArrayList<String>();
+ for (String userId : new IterableIterator<String>(mPublicKey.getUserIDs())) {
+ userIds.add(userId);
+ }
+ return userIds;
+ }
+
+ public boolean isElGamalEncrypt() {
+ return getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT;
+ }
+
+ public boolean isDSA() {
+ return getAlgorithm() == PGPPublicKey.DSA;
+ }
+
+ @SuppressWarnings("unchecked")
+ public int getKeyUsage() {
+ if(mCacheUsage == null) {
+ mCacheUsage = 0;
+ if (mPublicKey.getVersion() >= 4) {
+ for (PGPSignature sig : new IterableIterator<PGPSignature>(mPublicKey.getSignatures())) {
+ if (mPublicKey.isMasterKey() && sig.getKeyID() != mPublicKey.getKeyID()) {
+ continue;
+ }
+
+ PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets();
+ if (hashed != null) {
+ mCacheUsage |= hashed.getKeyFlags();
+ }
+
+ PGPSignatureSubpacketVector unhashed = sig.getUnhashedSubPackets();
+ if (unhashed != null) {
+ mCacheUsage |= unhashed.getKeyFlags();
+ }
+ }
+ }
+ }
+ return mCacheUsage;
+ }
+
+ public boolean canAuthenticate() {
+ return mPublicKey.getVersion() <= 3 || (getKeyUsage() & KeyFlags.AUTHENTICATION) != 0;
+ }
+
+ public boolean canCertify() {
+ return mPublicKey.getVersion() <= 3 || (getKeyUsage() & KeyFlags.CERTIFY_OTHER) != 0;
+ }
+
+ public boolean canEncrypt() {
+ if (!mPublicKey.isEncryptionKey()) {
+ return false;
+ }
+
+ // special cases
+ if (mPublicKey.getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT) {
+ return true;
+ }
+
+ if (mPublicKey.getAlgorithm() == PGPPublicKey.RSA_ENCRYPT) {
+ return true;
+ }
+
+ return mPublicKey.getVersion() <= 3 ||
+ (getKeyUsage() & (KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE)) != 0;
+
+ }
+
+ public boolean canSign() {
+ // special case
+ if (mPublicKey.getAlgorithm() == PGPPublicKey.RSA_SIGN) {
+ return true;
+ }
+
+ return mPublicKey.getVersion() <= 3 || (getKeyUsage() & KeyFlags.SIGN_DATA) != 0;
+ }
+
+ public byte[] getFingerprint() {
+ return mPublicKey.getFingerprint();
+ }
+
+ // TODO This method should have package visibility - no access outside the pgp package!
+ // (It's still used in ProviderHelper at this point)
+ public PGPPublicKey getPublicKey() {
+ return mPublicKey;
+ }
+
+ public Iterator<WrappedSignature> getSignaturesForId(String userId) {
+ final Iterator<PGPSignature> it = mPublicKey.getSignaturesForID(userId);
+ return new Iterator<WrappedSignature>() {
+ public void remove() {
+ it.remove();
+ }
+ public WrappedSignature next() {
+ return new WrappedSignature(it.next());
+ }
+ public boolean hasNext() {
+ return it.hasNext();
+ }
+ };
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedSecretKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedSecretKey.java
new file mode 100644
index 000000000..0e14a7fd3
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedSecretKey.java
@@ -0,0 +1,33 @@
+package org.sufficientlysecure.keychain.pgp;
+
+import org.spongycastle.bcpg.sig.KeyFlags;
+import org.spongycastle.openpgp.PGPSecretKey;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+public class UncachedSecretKey extends UncachedPublicKey {
+
+ public static final int CERTIFY_OTHER = KeyFlags.CERTIFY_OTHER;
+ public static final int SIGN_DATA = KeyFlags.SIGN_DATA;
+ public static final int ENCRYPT_COMMS = KeyFlags.ENCRYPT_COMMS;
+ public static final int ENCRYPT_STORAGE = KeyFlags.ENCRYPT_STORAGE;
+ public static final int AUTHENTICATION = KeyFlags.AUTHENTICATION;
+
+ final PGPSecretKey mSecretKey;
+
+ public UncachedSecretKey(PGPSecretKey secretKey) {
+ super(secretKey.getPublicKey());
+ mSecretKey = secretKey;
+ }
+
+ @Deprecated
+ public PGPSecretKey getSecretKeyExternal() {
+ return mSecretKey;
+ }
+
+ public void encodeSecretKey(OutputStream os) throws IOException {
+ mSecretKey.encode(os);
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedKeyRing.java
new file mode 100644
index 000000000..2b6049894
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedKeyRing.java
@@ -0,0 +1,97 @@
+package org.sufficientlysecure.keychain.pgp;
+
+import org.spongycastle.openpgp.PGPKeyRing;
+import org.spongycastle.openpgp.PGPPublicKey;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.util.IterableIterator;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/** A generic wrapped PGPKeyRing object.
+ *
+ * This class provides implementations for all basic getters which both
+ * PublicKeyRing and SecretKeyRing have in common. To make the wrapped keyring
+ * class typesafe in implementing subclasses, the field is stored in the
+ * implementing class, providing properly typed access through the getRing
+ * getter method.
+ *
+ */
+public abstract class WrappedKeyRing extends KeyRing {
+
+ private final boolean mHasAnySecret;
+ private final int mVerified;
+
+ WrappedKeyRing(boolean hasAnySecret, int verified) {
+ mHasAnySecret = hasAnySecret;
+ mVerified = verified;
+ }
+
+ public long getMasterKeyId() {
+ return getRing().getPublicKey().getKeyID();
+ }
+
+ public boolean hasAnySecret() {
+ return mHasAnySecret;
+ }
+
+ public int getVerified() {
+ return mVerified;
+ }
+
+ public String getPrimaryUserId() throws PgpGeneralException {
+ return (String) getRing().getPublicKey().getUserIDs().next();
+ };
+
+ public boolean isRevoked() throws PgpGeneralException {
+ // Is the master key revoked?
+ return getRing().getPublicKey().isRevoked();
+ }
+
+ public boolean canCertify() throws PgpGeneralException {
+ return getRing().getPublicKey().isEncryptionKey();
+ }
+
+ public long getEncryptId() throws PgpGeneralException {
+ for(PGPPublicKey key : new IterableIterator<PGPPublicKey>(getRing().getPublicKeys())) {
+ if(PgpKeyHelper.isEncryptionKey(key)) {
+ return key.getKeyID();
+ }
+ }
+ throw new PgpGeneralException("No valid encryption key found!");
+ }
+
+ public boolean hasEncrypt() throws PgpGeneralException {
+ try {
+ getEncryptId();
+ return true;
+ } catch(PgpGeneralException e) {
+ return false;
+ }
+ }
+
+ public long getSignId() throws PgpGeneralException {
+ for(PGPPublicKey key : new IterableIterator<PGPPublicKey>(getRing().getPublicKeys())) {
+ if(PgpKeyHelper.isSigningKey(key)) {
+ return key.getKeyID();
+ }
+ }
+ throw new PgpGeneralException("No valid signing key found!");
+ }
+
+ public boolean hasSign() throws PgpGeneralException {
+ try {
+ getSignId();
+ return true;
+ } catch (PgpGeneralException e) {
+ return false;
+ }
+ }
+
+ public void encode(OutputStream stream) throws IOException {
+ getRing().encode(stream);
+ }
+
+ abstract PGPKeyRing getRing();
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedPublicKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedPublicKey.java
new file mode 100644
index 000000000..69a4fbdee
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedPublicKey.java
@@ -0,0 +1,39 @@
+package org.sufficientlysecure.keychain.pgp;
+
+import org.spongycastle.openpgp.PGPPublicKey;
+import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator;
+import org.sufficientlysecure.keychain.util.IterableIterator;
+
+/** Wrapper for a PGPPublicKey.
+ *
+ * The methods implemented in this class are a thin layer over
+ * UncachedPublicKey. The difference between the two classes is that objects of
+ * this class can only be obtained from a WrappedKeyRing, and that it stores a
+ * back reference to its parent as well. A method which works with
+ * WrappedPublicKey is therefore guaranteed to work on a KeyRing which is
+ * stored in the database.
+ *
+ */
+public class WrappedPublicKey extends UncachedPublicKey {
+
+ // this is the parent key ring
+ final KeyRing mRing;
+
+ WrappedPublicKey(KeyRing ring, PGPPublicKey key) {
+ super(key);
+ mRing = ring;
+ }
+
+ public IterableIterator<String> getUserIds() {
+ return new IterableIterator<String>(mPublicKey.getUserIDs());
+ }
+
+ public KeyRing getKeyRing() {
+ return mRing;
+ }
+
+ JcePublicKeyKeyEncryptionMethodGenerator getPubKeyEncryptionGenerator() {
+ return new JcePublicKeyKeyEncryptionMethodGenerator(mPublicKey);
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedPublicKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedPublicKeyRing.java
new file mode 100644
index 000000000..99dc99436
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedPublicKeyRing.java
@@ -0,0 +1,192 @@
+package org.sufficientlysecure.keychain.pgp;
+
+import org.spongycastle.bcpg.ArmoredOutputStream;
+import org.spongycastle.bcpg.SignatureSubpacketTags;
+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.PGPSignature;
+import org.spongycastle.openpgp.PGPSignatureList;
+import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
+import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.util.IterableIterator;
+import org.sufficientlysecure.keychain.util.Log;
+
+import java.io.IOException;
+import java.security.SignatureException;
+import java.util.Arrays;
+import java.util.Iterator;
+
+public class WrappedPublicKeyRing extends WrappedKeyRing {
+
+ private PGPPublicKeyRing mRing;
+ private final byte[] mPubKey;
+
+ public WrappedPublicKeyRing(byte[] blob, boolean hasAnySecret, int verified) {
+ super(hasAnySecret, verified);
+ mPubKey = blob;
+ }
+
+ PGPPublicKeyRing getRing() {
+ if(mRing == null) {
+ PGPObjectFactory factory = new PGPObjectFactory(mPubKey);
+ PGPKeyRing keyRing = null;
+ try {
+ if ((keyRing = (PGPKeyRing) factory.nextObject()) == null) {
+ Log.e(Constants.TAG, "No keys given!");
+ }
+ } catch (IOException e) {
+ Log.e(Constants.TAG, "Error while converting to PGPKeyRing!", e);
+ }
+
+ mRing = (PGPPublicKeyRing) keyRing;
+ }
+ return mRing;
+ }
+
+ public void encode(ArmoredOutputStream stream) throws IOException {
+ getRing().encode(stream);
+ }
+
+ public WrappedPublicKey getSubkey() {
+ return new WrappedPublicKey(this, getRing().getPublicKey());
+ }
+
+ public WrappedPublicKey getSubkey(long id) {
+ return new WrappedPublicKey(this, getRing().getPublicKey(id));
+ }
+
+ /** Getter that returns the subkey that should be used for signing. */
+ WrappedPublicKey getEncryptionSubKey() throws PgpGeneralException {
+ PGPPublicKey key = getRing().getPublicKey(getEncryptId());
+ if(key != null) {
+ WrappedPublicKey cKey = new WrappedPublicKey(this, key);
+ if(!cKey.canEncrypt()) {
+ throw new PgpGeneralException("key error");
+ }
+ return cKey;
+ }
+ // TODO handle with proper exception
+ throw new PgpGeneralException("no encryption key available");
+ }
+
+ public boolean verifySubkeyBinding(WrappedPublicKey cachedSubkey) {
+ boolean validSubkeyBinding = false;
+ boolean validTempSubkeyBinding = false;
+ boolean validPrimaryKeyBinding = false;
+
+ PGPPublicKey masterKey = getRing().getPublicKey();
+ PGPPublicKey subKey = cachedSubkey.getPublicKey();
+
+ // Is this the master key? Match automatically, then.
+ if(Arrays.equals(masterKey.getFingerprint(), subKey.getFingerprint())) {
+ return true;
+ }
+
+ JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
+ new JcaPGPContentVerifierBuilderProvider()
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+
+ Iterator<PGPSignature> itr = subKey.getSignatures();
+
+ 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
+ //either, so we will err on bad subkey binding here.
+ PGPSignature sig = itr.next();
+ if (sig.getKeyID() == masterKey.getKeyID() &&
+ sig.getSignatureType() == PGPSignature.SUBKEY_BINDING) {
+ //check and if ok, check primary key binding.
+ try {
+ sig.init(contentVerifierBuilderProvider, masterKey);
+ validTempSubkeyBinding = sig.verifyCertification(masterKey, subKey);
+ } catch (PGPException e) {
+ continue;
+ } catch (SignatureException e) {
+ continue;
+ }
+
+ if (validTempSubkeyBinding) {
+ validSubkeyBinding = true;
+ }
+ if (validTempSubkeyBinding) {
+ validPrimaryKeyBinding = verifyPrimaryKeyBinding(sig.getUnhashedSubPackets(),
+ masterKey, subKey);
+ if (validPrimaryKeyBinding) {
+ break;
+ }
+ validPrimaryKeyBinding = verifyPrimaryKeyBinding(sig.getHashedSubPackets(),
+ masterKey, subKey);
+ if (validPrimaryKeyBinding) {
+ break;
+ }
+ }
+ }
+ }
+ return validSubkeyBinding && validPrimaryKeyBinding;
+
+ }
+
+ 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)) {
+ try {
+ eSigList = pkts.getEmbeddedSignatures();
+ } catch (IOException e) {
+ return false;
+ } catch (PGPException e) {
+ return false;
+ }
+ for (int j = 0; j < eSigList.size(); ++j) {
+ PGPSignature emSig = eSigList.get(j);
+ if (emSig.getSignatureType() == PGPSignature.PRIMARYKEY_BINDING) {
+ try {
+ emSig.init(contentVerifierBuilderProvider, signingPublicKey);
+ validPrimaryKeyBinding = emSig.verifyCertification(masterPublicKey, signingPublicKey);
+ if (validPrimaryKeyBinding) {
+ break;
+ }
+ } catch (PGPException e) {
+ continue;
+ } catch (SignatureException e) {
+ continue;
+ }
+ }
+ }
+ }
+
+ return validPrimaryKeyBinding;
+ }
+
+ public IterableIterator<WrappedPublicKey> iterator() {
+ final Iterator<PGPPublicKey> it = getRing().getPublicKeys();
+ return new IterableIterator<WrappedPublicKey>(new Iterator<WrappedPublicKey>() {
+ @Override
+ public boolean hasNext() {
+ return it.hasNext();
+ }
+
+ @Override
+ public WrappedPublicKey next() {
+ return new WrappedPublicKey(WrappedPublicKeyRing.this, it.next());
+ }
+
+ @Override
+ public void remove() {
+ it.remove();
+ }
+ });
+ }
+
+} \ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKey.java
new file mode 100644
index 000000000..ef8044a9b
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKey.java
@@ -0,0 +1,200 @@
+package org.sufficientlysecure.keychain.pgp;
+
+import org.spongycastle.openpgp.PGPException;
+import org.spongycastle.openpgp.PGPPrivateKey;
+import org.spongycastle.openpgp.PGPPublicKey;
+import org.spongycastle.openpgp.PGPPublicKeyRing;
+import org.spongycastle.openpgp.PGPSecretKey;
+import org.spongycastle.openpgp.PGPSignature;
+import org.spongycastle.openpgp.PGPSignatureGenerator;
+import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator;
+import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
+import org.spongycastle.openpgp.PGPUtil;
+import org.spongycastle.openpgp.PGPV3SignatureGenerator;
+import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
+import org.spongycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
+import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
+import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
+import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException;
+import org.sufficientlysecure.keychain.util.IterableIterator;
+
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.SignatureException;
+import java.util.List;
+
+/** Wrapper for a PGPSecretKey.
+ *
+ * This object can only be obtained from a WrappedSecretKeyRing, and stores a
+ * back reference to its parent.
+ *
+ * This class represents known secret keys which are stored in the database.
+ * All "crypto operations using a known secret key" should be implemented in
+ * this class, to ensure on type level that these operations are performed on
+ * properly imported secret keys only.
+ *
+ */
+public class WrappedSecretKey extends WrappedPublicKey {
+
+ private final PGPSecretKey mSecretKey;
+ private PGPPrivateKey mPrivateKey = null;
+
+ WrappedSecretKey(WrappedSecretKeyRing ring, PGPSecretKey key) {
+ super(ring, key.getPublicKey());
+ mSecretKey = key;
+ }
+
+ public WrappedSecretKeyRing getRing() {
+ return (WrappedSecretKeyRing) mRing;
+ }
+
+ /** Returns the wrapped PGPSecretKeyRing.
+ * This function is for compatibility only, should not be used anymore and will be removed
+ */
+ @Deprecated
+ public PGPSecretKey getKeyExternal() {
+ return mSecretKey;
+ }
+
+ public boolean unlock(String passphrase) throws PgpGeneralException {
+ try {
+ PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
+ Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
+ mPrivateKey = mSecretKey.extractPrivateKey(keyDecryptor);
+ } catch (PGPException e) {
+ return false;
+ }
+ if(mPrivateKey == null) {
+ throw new PgpGeneralException("error extracting key");
+ }
+ return true;
+ }
+
+ public PGPSignatureGenerator getSignatureGenerator(int hashAlgo, boolean cleartext)
+ throws PgpGeneralException {
+ if(mPrivateKey == null) {
+ throw new PrivateKeyNotUnlockedException();
+ }
+
+ // content signer based on signing key algorithm and chosen hash algorithm
+ JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(
+ mSecretKey.getPublicKey().getAlgorithm(), hashAlgo)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+
+ int signatureType;
+ if (cleartext) {
+ // for sign-only ascii text
+ signatureType = PGPSignature.CANONICAL_TEXT_DOCUMENT;
+ } else {
+ signatureType = PGPSignature.BINARY_DOCUMENT;
+ }
+
+ try {
+ PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
+ signatureGenerator.init(signatureType, mPrivateKey);
+
+ PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
+ spGen.setSignerUserID(false, mRing.getPrimaryUserId());
+ signatureGenerator.setHashedSubpackets(spGen.generate());
+ return signatureGenerator;
+ } catch(PGPException e) {
+ throw new PgpGeneralException("Error initializing signature!", e);
+ }
+ }
+
+ public PGPV3SignatureGenerator getV3SignatureGenerator(int hashAlgo, boolean cleartext)
+ throws PgpGeneralException {
+ if(mPrivateKey == null) {
+ throw new PrivateKeyNotUnlockedException();
+ }
+
+ // content signer based on signing key algorithm and chosen hash algorithm
+ JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(
+ mSecretKey.getPublicKey().getAlgorithm(), hashAlgo)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+
+ int signatureType;
+ if (cleartext) {
+ // for sign-only ascii text
+ signatureType = PGPSignature.CANONICAL_TEXT_DOCUMENT;
+ } else {
+ signatureType = PGPSignature.BINARY_DOCUMENT;
+ }
+
+ try {
+ PGPV3SignatureGenerator signatureV3Generator = new PGPV3SignatureGenerator(contentSignerBuilder);
+ signatureV3Generator.init(signatureType, mPrivateKey);
+ return signatureV3Generator;
+ } catch(PGPException e) {
+ throw new PgpGeneralException("Error initializing signature!", e);
+ }
+ }
+
+ public PublicKeyDataDecryptorFactory getDecryptorFactory() {
+ if(mPrivateKey == null) {
+ throw new PrivateKeyNotUnlockedException();
+ }
+ return new JcePublicKeyDataDecryptorFactoryBuilder()
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(mPrivateKey);
+ }
+
+ /**
+ * Certify the given pubkeyid with the given masterkeyid.
+ *
+ * @param publicKeyRing Keyring to add certification to.
+ * @param userIds User IDs to certify, must not be null or empty
+ * @return A keyring with added certifications
+ */
+ public UncachedKeyRing certifyUserIds(WrappedPublicKeyRing publicKeyRing, List<String> userIds)
+ throws PgpGeneralMsgIdException, NoSuchAlgorithmException, NoSuchProviderException,
+ PGPException, SignatureException {
+
+ if(mPrivateKey == null) {
+ throw new PrivateKeyNotUnlockedException();
+ }
+
+ // create a signatureGenerator from the supplied masterKeyId and passphrase
+ PGPSignatureGenerator signatureGenerator;
+ {
+ // TODO: SHA256 fixed?
+ JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(
+ mSecretKey.getPublicKey().getAlgorithm(), PGPUtil.SHA256)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+
+ signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
+ signatureGenerator.init(PGPSignature.DEFAULT_CERTIFICATION, mPrivateKey);
+ }
+
+ { // supply signatureGenerator with a SubpacketVector
+ PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
+ PGPSignatureSubpacketVector packetVector = spGen.generate();
+ signatureGenerator.setHashedSubpackets(packetVector);
+ }
+
+ // get the master subkey (which we certify for)
+ PGPPublicKey publicKey = publicKeyRing.getSubkey().getPublicKey();
+
+ // 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);
+ }
+
+ PGPPublicKeyRing ring = PGPPublicKeyRing.insertPublicKey(publicKeyRing.getRing(), publicKey);
+
+ return new UncachedKeyRing(ring);
+ }
+
+ static class PrivateKeyNotUnlockedException extends RuntimeException {
+ // this exception is a programming error which happens when an operation which requires
+ // the private key is called without a previous call to unlock()
+ }
+
+ public UncachedSecretKey getUncached() {
+ return new UncachedSecretKey(mSecretKey);
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKeyRing.java
new file mode 100644
index 000000000..91d4286f4
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKeyRing.java
@@ -0,0 +1,141 @@
+package org.sufficientlysecure.keychain.pgp;
+
+import org.spongycastle.openpgp.PGPException;
+import org.spongycastle.openpgp.PGPKeyRing;
+import org.spongycastle.openpgp.PGPObjectFactory;
+import org.spongycastle.openpgp.PGPPrivateKey;
+import org.spongycastle.openpgp.PGPPublicKeyRing;
+import org.spongycastle.openpgp.PGPSecretKey;
+import org.spongycastle.openpgp.PGPSecretKeyRing;
+import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
+import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder;
+import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
+import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.util.IterableIterator;
+import org.sufficientlysecure.keychain.util.Log;
+
+import java.io.IOException;
+import java.security.NoSuchProviderException;
+import java.util.Iterator;
+
+public class WrappedSecretKeyRing extends WrappedKeyRing {
+
+ private PGPSecretKeyRing mRing;
+
+ public WrappedSecretKeyRing(byte[] blob, boolean isRevoked, int verified)
+ {
+ super(isRevoked, verified);
+ PGPObjectFactory factory = new PGPObjectFactory(blob);
+ PGPKeyRing keyRing = null;
+ try {
+ if ((keyRing = (PGPKeyRing) factory.nextObject()) == null) {
+ Log.e(Constants.TAG, "No keys given!");
+ }
+ } catch (IOException e) {
+ Log.e(Constants.TAG, "Error while converting to PGPKeyRing!", e);
+ }
+
+ mRing = (PGPSecretKeyRing) keyRing;
+ }
+
+ PGPSecretKeyRing getRing() {
+ return mRing;
+ }
+
+ public WrappedSecretKey getSubKey() {
+ return new WrappedSecretKey(this, mRing.getSecretKey());
+ }
+
+ public WrappedSecretKey getSubKey(long id) {
+ return new WrappedSecretKey(this, mRing.getSecretKey(id));
+ }
+
+ /** Getter that returns the subkey that should be used for signing. */
+ WrappedSecretKey getSigningSubKey() throws PgpGeneralException {
+ PGPSecretKey key = mRing.getSecretKey(getSignId());
+ if(key != null) {
+ WrappedSecretKey cKey = new WrappedSecretKey(this, key);
+ if(!cKey.canSign()) {
+ throw new PgpGeneralException("key error");
+ }
+ return cKey;
+ }
+ // TODO handle with proper exception
+ throw new PgpGeneralException("no signing key available");
+ }
+
+ public boolean hasPassphrase() {
+ PGPSecretKey secretKey = null;
+ boolean foundValidKey = false;
+ for (Iterator keys = mRing.getSecretKeys(); keys.hasNext(); ) {
+ secretKey = (PGPSecretKey) keys.next();
+ if (!secretKey.isPrivateKeyEmpty()) {
+ foundValidKey = true;
+ break;
+ }
+ }
+ if(!foundValidKey) {
+ return false;
+ }
+
+ try {
+ PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
+ .setProvider("SC").build("".toCharArray());
+ PGPPrivateKey testKey = secretKey.extractPrivateKey(keyDecryptor);
+ return testKey == null;
+ } catch(PGPException e) {
+ // this means the crc check failed -> passphrase required
+ return true;
+ }
+ }
+
+ public UncachedKeyRing changeSecretKeyPassphrase(String oldPassphrase,
+ String newPassphrase)
+ throws IOException, PGPException, NoSuchProviderException {
+
+ if (oldPassphrase == null) {
+ oldPassphrase = "";
+ }
+ if (newPassphrase == null) {
+ newPassphrase = "";
+ }
+
+ PGPSecretKeyRing newKeyRing = PGPSecretKeyRing.copyWithNewPassword(
+ mRing,
+ new JcePBESecretKeyDecryptorBuilder(new JcaPGPDigestCalculatorProviderBuilder()
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build()).setProvider(
+ Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(oldPassphrase.toCharArray()),
+ new JcePBESecretKeyEncryptorBuilder(mRing.getSecretKey()
+ .getKeyEncryptionAlgorithm()).build(newPassphrase.toCharArray()));
+
+ return new UncachedKeyRing(newKeyRing);
+
+ }
+
+ public IterableIterator<WrappedSecretKey> iterator() {
+ final Iterator<PGPSecretKey> it = mRing.getSecretKeys();
+ return new IterableIterator<WrappedSecretKey>(new Iterator<WrappedSecretKey>() {
+ @Override
+ public boolean hasNext() {
+ return it.hasNext();
+ }
+
+ @Override
+ public WrappedSecretKey next() {
+ return new WrappedSecretKey(WrappedSecretKeyRing.this, it.next());
+ }
+
+ @Override
+ public void remove() {
+ it.remove();
+ }
+ });
+ }
+
+ public UncachedKeyRing getUncached() {
+ return new UncachedKeyRing(mRing);
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java
new file mode 100644
index 000000000..1b7a5e8ba
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java
@@ -0,0 +1,161 @@
+package org.sufficientlysecure.keychain.pgp;
+
+import org.spongycastle.bcpg.SignatureSubpacket;
+import org.spongycastle.bcpg.SignatureSubpacketTags;
+import org.spongycastle.bcpg.sig.RevocationReason;
+import org.spongycastle.openpgp.PGPException;
+import org.spongycastle.openpgp.PGPObjectFactory;
+import org.spongycastle.openpgp.PGPPublicKey;
+import org.spongycastle.openpgp.PGPSignature;
+import org.spongycastle.openpgp.PGPSignatureList;
+import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.util.Log;
+
+import java.io.IOException;
+import java.security.SignatureException;
+import java.util.Date;
+
+/** OpenKeychain wrapper around PGPSignature objects.
+ *
+ * This is a mostly simple wrapper around a single bouncycastle PGPSignature
+ * object. It exposes high level getters for all relevant information, methods
+ * for verification of various signatures (uid binding, subkey binding, generic
+ * bytes), and a static method for construction from bytes.
+ *
+ */
+public class WrappedSignature {
+
+ public static final int DEFAULT_CERTIFICATION = PGPSignature.DEFAULT_CERTIFICATION;
+ public static final int NO_CERTIFICATION = PGPSignature.NO_CERTIFICATION;
+ public static final int CASUAL_CERTIFICATION = PGPSignature.CASUAL_CERTIFICATION;
+ public static final int POSITIVE_CERTIFICATION = PGPSignature.POSITIVE_CERTIFICATION;
+ public static final int CERTIFICATION_REVOCATION = PGPSignature.CERTIFICATION_REVOCATION;
+
+ final PGPSignature mSig;
+
+ protected WrappedSignature(PGPSignature sig) {
+ mSig = sig;
+ }
+
+ public long getKeyId() {
+ return mSig.getKeyID();
+ }
+
+ public int getSignatureType() {
+ return mSig.getSignatureType();
+ }
+
+ public int getKeyAlgorithm() {
+ return mSig.getKeyAlgorithm();
+ }
+
+ public Date getCreationTime() {
+ return mSig.getCreationTime();
+ }
+
+ public byte[] getEncoded() throws IOException {
+ return mSig.getEncoded();
+ }
+
+ public boolean isRevocation() {
+ return mSig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.REVOCATION_REASON);
+ }
+
+ public boolean isPrimaryUserId() {
+ return mSig.getHashedSubPackets().isPrimaryUserID();
+ }
+
+ public String getRevocationReason() throws PgpGeneralException {
+ if(!isRevocation()) {
+ throw new PgpGeneralException("Not a revocation signature.");
+ }
+ SignatureSubpacket p = mSig.getHashedSubPackets().getSubpacket(
+ SignatureSubpacketTags.REVOCATION_REASON);
+ // For some reason, this is missing in SignatureSubpacketInputStream:146
+ if (!(p instanceof RevocationReason)) {
+ p = new RevocationReason(false, p.getData());
+ }
+ return ((RevocationReason) p).getRevocationDescription();
+ }
+
+ public void init(WrappedPublicKey key) throws PgpGeneralException {
+ init(key.getPublicKey());
+ }
+
+ public void init(UncachedPublicKey key) throws PgpGeneralException {
+ init(key.getPublicKey());
+ }
+
+ protected void init(PGPPublicKey key) throws PgpGeneralException {
+ try {
+ JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
+ new JcaPGPContentVerifierBuilderProvider()
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ mSig.init(contentVerifierBuilderProvider, key);
+ } catch(PGPException e) {
+ throw new PgpGeneralException(e);
+ }
+ }
+
+ public void update(byte[] data, int offset, int length) throws PgpGeneralException {
+ try {
+ mSig.update(data, offset, length);
+ } catch(SignatureException e) {
+ throw new PgpGeneralException(e);
+ }
+ }
+
+ public void update(byte data) throws PgpGeneralException {
+ try {
+ mSig.update(data);
+ } catch(SignatureException e) {
+ throw new PgpGeneralException(e);
+ }
+ }
+
+ public boolean verify() throws PgpGeneralException {
+ try {
+ return mSig.verify();
+ } catch(SignatureException e) {
+ throw new PgpGeneralException(e);
+ } catch(PGPException e) {
+ throw new PgpGeneralException(e);
+ }
+ }
+
+ protected boolean verifySignature(PGPPublicKey key, String uid) throws PgpGeneralException {
+ try {
+ return mSig.verifyCertification(uid, key);
+ } catch (SignatureException e) {
+ throw new PgpGeneralException("Error!", e);
+ } catch (PGPException e) {
+ throw new PgpGeneralException("Error!", e);
+ }
+ }
+
+ public boolean verifySignature(UncachedPublicKey key, String uid) throws PgpGeneralException {
+ return verifySignature(key.getPublicKey(), uid);
+ }
+ public boolean verifySignature(WrappedPublicKey key, String uid) throws PgpGeneralException {
+ return verifySignature(key.getPublicKey(), uid);
+ }
+
+ public static WrappedSignature fromBytes(byte[] data) {
+ PGPObjectFactory factory = new PGPObjectFactory(data);
+ PGPSignatureList signatures = null;
+ try {
+ if ((signatures = (PGPSignatureList) factory.nextObject()) == null || signatures.isEmpty()) {
+ Log.e(Constants.TAG, "No signatures given!");
+ return null;
+ }
+ } catch (IOException e) {
+ Log.e(Constants.TAG, "Error while converting to PGPSignature!", e);
+ return null;
+ }
+
+ return new WrappedSignature(signatures.get(0));
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralException.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralException.java
index 37d21eea4..f37a61852 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralException.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralException.java
@@ -27,4 +27,7 @@ public class PgpGeneralException extends Exception {
public PgpGeneralException(String message, Throwable cause) {
super(message, cause);
}
+ public PgpGeneralException(Throwable cause) {
+ super(cause);
+ }
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java
new file mode 100644
index 000000000..48d40430a
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java
@@ -0,0 +1,161 @@
+package org.sufficientlysecure.keychain.provider;
+
+import android.net.Uri;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.pgp.KeyRing;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.util.Log;
+
+/** This implementation of KeyRing provides a cached view of PublicKeyRing
+ * objects based on database queries exclusively.
+ *
+ * This class should be used where only few points of data but no actual
+ * cryptographic operations are required about a PublicKeyRing which is already
+ * in the database. This happens commonly in UI code, where parsing of a PGP
+ * key for examination would be a very expensive operation.
+ *
+ * Each getter method is implemented using a more or less expensive database
+ * query, while object construction is (almost) free. A common pattern is
+ * mProviderHelper.getCachedKeyRing(uri).getterMethod()
+ *
+ * TODO Ensure that the values returned here always match the ones returned by
+ * the parsed KeyRing!
+ *
+ */
+public class CachedPublicKeyRing extends KeyRing {
+
+ final ProviderHelper mProviderHelper;
+ final Uri mUri;
+
+ public CachedPublicKeyRing(ProviderHelper providerHelper, Uri uri) {
+ mProviderHelper = providerHelper;
+ mUri = uri;
+ }
+
+ public long getMasterKeyId() throws PgpGeneralException {
+ try {
+ Object data = mProviderHelper.getGenericData(mUri,
+ KeychainContract.KeyRings.MASTER_KEY_ID, ProviderHelper.FIELD_TYPE_INTEGER);
+ return (Long) data;
+ } catch (ProviderHelper.NotFoundException e) {
+ throw new PgpGeneralException(e);
+ }
+ }
+
+ /**
+ * Find the master key id related to a given query. The id will either be extracted from the
+ * query, which should work for all specific /key_rings/ queries, or will be queried if it can't.
+ */
+ public long extractOrGetMasterKeyId() throws PgpGeneralException {
+ // try extracting from the uri first
+ String firstSegment = mUri.getPathSegments().get(1);
+ if (!firstSegment.equals("find")) try {
+ return Long.parseLong(firstSegment);
+ } catch (NumberFormatException e) {
+ // didn't work? oh well.
+ Log.d(Constants.TAG, "Couldn't get masterKeyId from URI, querying...");
+ }
+ return getMasterKeyId();
+ }
+
+ public String getPrimaryUserId() throws PgpGeneralException {
+ try {
+ Object data = mProviderHelper.getGenericData(mUri,
+ KeychainContract.KeyRings.MASTER_KEY_ID,
+ ProviderHelper.FIELD_TYPE_STRING);
+ return (String) data;
+ } catch(ProviderHelper.NotFoundException e) {
+ throw new PgpGeneralException(e);
+ }
+ }
+
+ public boolean isRevoked() throws PgpGeneralException {
+ try {
+ Object data = mProviderHelper.getGenericData(mUri,
+ KeychainContract.KeyRings.MASTER_KEY_ID,
+ ProviderHelper.FIELD_TYPE_INTEGER);
+ return (Long) data > 0;
+ } catch(ProviderHelper.NotFoundException e) {
+ throw new PgpGeneralException(e);
+ }
+ }
+
+ public boolean canCertify() throws PgpGeneralException {
+ try {
+ Object data = mProviderHelper.getGenericData(mUri,
+ KeychainContract.KeyRings.MASTER_KEY_ID,
+ ProviderHelper.FIELD_TYPE_INTEGER);
+ return (Long) data > 0;
+ } catch(ProviderHelper.NotFoundException e) {
+ throw new PgpGeneralException(e);
+ }
+ }
+
+ public long getEncryptId() throws PgpGeneralException {
+ try {
+ Object data = mProviderHelper.getGenericData(mUri,
+ KeychainContract.KeyRings.MASTER_KEY_ID,
+ ProviderHelper.FIELD_TYPE_INTEGER);
+ return (Long) data;
+ } catch(ProviderHelper.NotFoundException e) {
+ throw new PgpGeneralException(e);
+ }
+ }
+
+ public boolean hasEncrypt() throws PgpGeneralException {
+ try {
+ Object data = mProviderHelper.getGenericData(mUri,
+ KeychainContract.KeyRings.MASTER_KEY_ID,
+ ProviderHelper.FIELD_TYPE_INTEGER);
+ return (Long) data > 0;
+ } catch(ProviderHelper.NotFoundException e) {
+ throw new PgpGeneralException(e);
+ }
+ }
+
+ public long getSignId() throws PgpGeneralException {
+ try {
+ Object data = mProviderHelper.getGenericData(mUri,
+ KeychainContract.KeyRings.MASTER_KEY_ID,
+ ProviderHelper.FIELD_TYPE_INTEGER);
+ return (Long) data;
+ } catch(ProviderHelper.NotFoundException e) {
+ throw new PgpGeneralException(e);
+ }
+ }
+
+ public boolean hasSign() throws PgpGeneralException {
+ try {
+ Object data = mProviderHelper.getGenericData(mUri,
+ KeychainContract.KeyRings.MASTER_KEY_ID,
+ ProviderHelper.FIELD_TYPE_INTEGER);
+ return (Long) data > 0;
+ } catch(ProviderHelper.NotFoundException e) {
+ throw new PgpGeneralException(e);
+ }
+ }
+
+ public int getVerified() throws PgpGeneralException {
+ try {
+ Object data = mProviderHelper.getGenericData(mUri,
+ KeychainContract.KeyRings.MASTER_KEY_ID,
+ ProviderHelper.FIELD_TYPE_INTEGER);
+ return (Integer) data;
+ } catch(ProviderHelper.NotFoundException e) {
+ throw new PgpGeneralException(e);
+ }
+ }
+
+ public boolean hasAnySecret() throws PgpGeneralException {
+ try {
+ Object data = mProviderHelper.getGenericData(mUri,
+ KeychainContract.KeyRings.MASTER_KEY_ID,
+ ProviderHelper.FIELD_TYPE_INTEGER);
+ return (Long) data > 0;
+ } catch(ProviderHelper.NotFoundException e) {
+ throw new PgpGeneralException(e);
+ }
+
+ }
+}
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 a4fa3dac9..483f762f7 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java
@@ -110,6 +110,8 @@ public class KeychainContract {
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";
+ public static final String PUBKEY_DATA = "pubkey_data";
+ public static final String PRIVKEY_DATA = "privkey_data";
public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
.appendPath(BASE_KEY_RINGS).build();
@@ -123,6 +125,10 @@ public class KeychainContract {
return CONTENT_URI.buildUpon().appendPath(PATH_UNIFIED).build();
}
+ public static Uri buildGenericKeyRingUri(long masterKeyId) {
+ return CONTENT_URI.buildUpon().appendPath(Long.toString(masterKeyId)).build();
+ }
+
public static Uri buildGenericKeyRingUri(String masterKeyId) {
return CONTENT_URI.buildUpon().appendPath(masterKeyId).build();
}
@@ -131,20 +137,24 @@ public class KeychainContract {
return CONTENT_URI.buildUpon().appendPath(uri.getPathSegments().get(1)).build();
}
- public static Uri buildUnifiedKeyRingUri(String masterKeyId) {
- return CONTENT_URI.buildUpon().appendPath(masterKeyId).appendPath(PATH_UNIFIED).build();
+ public static Uri buildUnifiedKeyRingUri(long masterKeyId) {
+ return CONTENT_URI.buildUpon().appendPath(Long.toString(masterKeyId))
+ .appendPath(PATH_UNIFIED).build();
}
public static Uri buildUnifiedKeyRingUri(Uri uri) {
- return CONTENT_URI.buildUpon().appendPath(uri.getPathSegments().get(1)).appendPath(PATH_UNIFIED).build();
+ return CONTENT_URI.buildUpon().appendPath(uri.getPathSegments().get(1))
+ .appendPath(PATH_UNIFIED).build();
}
public static Uri buildUnifiedKeyRingsFindByEmailUri(String email) {
- return CONTENT_URI.buildUpon().appendPath(PATH_FIND).appendPath(PATH_BY_EMAIL).appendPath(email).build();
+ return CONTENT_URI.buildUpon().appendPath(PATH_FIND)
+ .appendPath(PATH_BY_EMAIL).appendPath(email).build();
}
- public static Uri buildUnifiedKeyRingsFindBySubkeyUri(String subkey) {
- return CONTENT_URI.buildUpon().appendPath(PATH_FIND).appendPath(PATH_BY_SUBKEY).appendPath(subkey).build();
+ public static Uri buildUnifiedKeyRingsFindBySubkeyUri(long subkey) {
+ return CONTENT_URI.buildUpon().appendPath(PATH_FIND)
+ .appendPath(PATH_BY_SUBKEY).appendPath(Long.toString(subkey)).build();
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java
index 68726d3e0..ceaa93f9b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java
@@ -23,11 +23,9 @@ import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.provider.BaseColumns;
-import org.spongycastle.openpgp.PGPKeyRing;
-import org.spongycastle.openpgp.PGPPublicKeyRing;
-import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
+import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAppsAccountsColumns;
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAppsColumns;
import org.sufficientlysecure.keychain.provider.KeychainContract.CertsColumns;
@@ -256,6 +254,8 @@ public class KeychainDatabase extends SQLiteOpenHelper {
}.getReadableDatabase();
Cursor cursor = null;
+ ProviderHelper providerHelper = new ProviderHelper(context);
+
try {
// we insert in two steps: first, all public keys that have secret keys
cursor = db.rawQuery("SELECT key_ring_data FROM key_rings WHERE type = 1 OR EXISTS ("
@@ -266,14 +266,11 @@ public class KeychainDatabase extends SQLiteOpenHelper {
for (int i = 0; i < cursor.getCount(); i++) {
cursor.moveToPosition(i);
byte[] data = cursor.getBlob(0);
- PGPKeyRing ring = PgpConversionHelper.BytesToPGPKeyRing(data);
- ProviderHelper providerHelper = new ProviderHelper(context);
- if (ring instanceof PGPPublicKeyRing)
- providerHelper.saveKeyRing((PGPPublicKeyRing) ring);
- else if (ring instanceof PGPSecretKeyRing)
- providerHelper.saveKeyRing((PGPSecretKeyRing) ring);
- else {
- Log.e(Constants.TAG, "Unknown blob data type!");
+ try {
+ UncachedKeyRing ring = UncachedKeyRing.decodeFromData(data);
+ providerHelper.savePublicKeyRing(ring);
+ } catch(PgpGeneralException e) {
+ Log.e(Constants.TAG, "Error decoding keyring blob!");
}
}
}
@@ -293,14 +290,11 @@ public class KeychainDatabase extends SQLiteOpenHelper {
for (int i = 0; i < cursor.getCount(); i++) {
cursor.moveToPosition(i);
byte[] data = cursor.getBlob(0);
- PGPKeyRing ring = PgpConversionHelper.BytesToPGPKeyRing(data);
- ProviderHelper providerHelper = new ProviderHelper(context);
- if (ring instanceof PGPPublicKeyRing) {
- providerHelper.saveKeyRing((PGPPublicKeyRing) ring);
- } else if (ring instanceof PGPSecretKeyRing) {
- providerHelper.saveKeyRing((PGPSecretKeyRing) ring);
- } else {
- Log.e(Constants.TAG, "Unknown blob data type!");
+ try {
+ UncachedKeyRing ring = UncachedKeyRing.decodeFromData(data);
+ providerHelper.savePublicKeyRing(ring);
+ } catch(PgpGeneralException e) {
+ Log.e(Constants.TAG, "Error decoding keyring blob!");
}
}
}
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 ec7bf58d9..b651069e9 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
@@ -43,6 +43,7 @@ import org.sufficientlysecure.keychain.util.Log;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
+import java.util.List;
public class KeychainProvider extends ContentProvider {
@@ -242,45 +243,39 @@ public class KeychainProvider extends ContentProvider {
HashMap<String, String> projectionMap = new HashMap<String, String>();
projectionMap.put(KeyRings._ID, Tables.KEYS + ".oid AS _id");
projectionMap.put(KeyRings.MASTER_KEY_ID, Tables.KEYS + "." + Keys.MASTER_KEY_ID);
- projectionMap.put(KeyRings.KEY_ID, Keys.KEY_ID);
- projectionMap.put(KeyRings.KEY_SIZE, Keys.KEY_SIZE);
+ projectionMap.put(KeyRings.KEY_ID, Tables.KEYS + "." + Keys.KEY_ID);
+ projectionMap.put(KeyRings.KEY_SIZE, Tables.KEYS + "." + Keys.KEY_SIZE);
projectionMap.put(KeyRings.IS_REVOKED, Tables.KEYS + "." + Keys.IS_REVOKED);
- projectionMap.put(KeyRings.CAN_CERTIFY, Keys.CAN_CERTIFY);
- projectionMap.put(KeyRings.CAN_ENCRYPT, Keys.CAN_ENCRYPT);
- projectionMap.put(KeyRings.CAN_SIGN, Keys.CAN_SIGN);
+ projectionMap.put(KeyRings.CAN_CERTIFY, Tables.KEYS + "." + Keys.CAN_CERTIFY);
+ projectionMap.put(KeyRings.CAN_ENCRYPT, Tables.KEYS + "." + Keys.CAN_ENCRYPT);
+ projectionMap.put(KeyRings.CAN_SIGN, Tables.KEYS + "." + Keys.CAN_SIGN);
projectionMap.put(KeyRings.CREATION, Tables.KEYS + "." + Keys.CREATION);
- projectionMap.put(KeyRings.EXPIRY, Keys.EXPIRY);
- projectionMap.put(KeyRings.ALGORITHM, Keys.ALGORITHM);
- projectionMap.put(KeyRings.FINGERPRINT, Keys.FINGERPRINT);
+ projectionMap.put(KeyRings.EXPIRY, Tables.KEYS + "." + Keys.EXPIRY);
+ projectionMap.put(KeyRings.ALGORITHM, Tables.KEYS + "." + Keys.ALGORITHM);
+ projectionMap.put(KeyRings.FINGERPRINT, Tables.KEYS + "." + Keys.FINGERPRINT);
projectionMap.put(KeyRings.USER_ID, UserIds.USER_ID);
projectionMap.put(KeyRings.VERIFIED, KeyRings.VERIFIED);
- projectionMap.put(KeyRings.HAS_SECRET, KeyRings.HAS_SECRET);
+ projectionMap.put(KeyRings.PUBKEY_DATA,
+ Tables.KEY_RINGS_PUBLIC + "." + KeyRingData.KEY_RING_DATA
+ + " AS " + KeyRings.PUBKEY_DATA);
+ projectionMap.put(KeyRings.PRIVKEY_DATA,
+ Tables.KEY_RINGS_SECRET + "." + KeyRingData.KEY_RING_DATA
+ + " AS " + KeyRings.PRIVKEY_DATA);
+ projectionMap.put(KeyRings.HAS_SECRET, Tables.KEYS + "." + KeyRings.HAS_SECRET);
projectionMap.put(KeyRings.HAS_ANY_SECRET,
"(EXISTS (SELECT * FROM " + Tables.KEY_RINGS_SECRET
+ " WHERE " + Tables.KEY_RINGS_SECRET + "." + KeyRingData.MASTER_KEY_ID
+ " = " + Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ ")) AS " + KeyRings.HAS_ANY_SECRET);
projectionMap.put(KeyRings.HAS_ENCRYPT,
- "(EXISTS (SELECT * FROM " + Tables.KEYS + " AS k"
- +" WHERE k." + Keys.MASTER_KEY_ID
- + " = " + Tables.KEYS + "." + Keys.MASTER_KEY_ID
- + " AND k." + Keys.IS_REVOKED + " = 0"
- + " AND k." + Keys.CAN_ENCRYPT + " = 1"
- + " AND ( k." + Keys.EXPIRY + " IS NULL OR k." + Keys.EXPIRY
- + " >= " + new Date().getTime() / 1000 + " )"
- + ")) AS " + KeyRings.HAS_ENCRYPT);
+ "kE." + Keys.KEY_ID + " AS " + KeyRings.HAS_ENCRYPT);
projectionMap.put(KeyRings.HAS_SIGN,
- "(EXISTS (SELECT * FROM " + Tables.KEYS + " AS k"
- +" WHERE k." + Keys.MASTER_KEY_ID
- + " = " + Tables.KEYS + "." + Keys.MASTER_KEY_ID
- + " AND k." + Keys.IS_REVOKED + " = 0"
- + " AND k." + Keys.HAS_SECRET + " = 1"
- + " AND k." + Keys.CAN_SIGN + " = 1"
- + " AND ( k." + Keys.EXPIRY + " IS NULL OR k." + Keys.EXPIRY
- + " >= " + new Date().getTime() / 1000 + " )"
- + ")) AS " + KeyRings.HAS_SIGN);
+ "kS." + Keys.KEY_ID + " AS " + KeyRings.HAS_SIGN);
qb.setProjectionMap(projectionMap);
+ // Need this as list so we can search in it
+ List<String> plist = Arrays.asList(projection);
+
qb.setTables(
Tables.KEYS
+ " INNER JOIN " + Tables.USER_IDS + " ON ("
@@ -295,6 +290,37 @@ public class KeychainProvider extends ContentProvider {
+ " AND " + Tables.CERTS + "." + Certs.VERIFIED
+ " = " + Certs.VERIFIED_SECRET
+ ")"
+ // fairly expensive joins following, only do when requested
+ + (plist.contains(KeyRings.PUBKEY_DATA) ?
+ " INNER JOIN " + Tables.KEY_RINGS_PUBLIC + " ON ("
+ + Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ + " = "
+ + Tables.KEY_RINGS_PUBLIC + "." + KeyRingData.MASTER_KEY_ID
+ + ")" : "")
+ + (plist.contains(KeyRings.PRIVKEY_DATA) ?
+ " LEFT JOIN " + Tables.KEY_RINGS_SECRET + " ON ("
+ + Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ + " = "
+ + Tables.KEY_RINGS_SECRET + "." + KeyRingData.MASTER_KEY_ID
+ + ")" : "")
+ + (plist.contains(KeyRings.HAS_ENCRYPT) ?
+ " LEFT JOIN " + Tables.KEYS + " AS kE ON ("
+ +"kE." + Keys.MASTER_KEY_ID
+ + " = " + Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ + " AND kE." + Keys.IS_REVOKED + " = 0"
+ + " AND kE." + Keys.CAN_ENCRYPT + " = 1"
+ + " AND ( kE." + Keys.EXPIRY + " IS NULL OR kE." + Keys.EXPIRY
+ + " >= " + new Date().getTime() / 1000 + " )"
+ + ")" : "")
+ + (plist.contains(KeyRings.HAS_SIGN) ?
+ " LEFT JOIN " + Tables.KEYS + " AS kS ON ("
+ +"kS." + Keys.MASTER_KEY_ID
+ + " = " + Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ + " AND kS." + Keys.IS_REVOKED + " = 0"
+ + " AND kS." + Keys.CAN_SIGN + " = 1"
+ + " AND ( kS." + Keys.EXPIRY + " IS NULL OR kS." + Keys.EXPIRY
+ + " >= " + new Date().getTime() / 1000 + " )"
+ + ")" : "")
);
qb.appendWhere(Tables.KEYS + "." + Keys.RANK + " = 0");
// in case there are multiple verifying certificates
@@ -595,7 +621,7 @@ public class KeychainProvider extends ContentProvider {
case KEY_RING_CERTS:
// we replace here, keeping only the latest signature
- // TODO this would be better handled in saveKeyRing directly!
+ // TODO this would be better handled in savePublicKeyRing directly!
db.replaceOrThrow(Tables.CERTS, null, values);
keyId = values.getAsLong(Certs.MASTER_KEY_ID);
break;
@@ -618,7 +644,7 @@ public class KeychainProvider extends ContentProvider {
}
if(keyId != null) {
- uri = KeyRings.buildGenericKeyRingUri(keyId.toString());
+ uri = KeyRings.buildGenericKeyRingUri(keyId);
rowUri = uri;
}
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 ab00db13a..043c40b25 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
@@ -23,26 +23,20 @@ import android.content.ContentValues;
import android.content.Context;
import android.content.OperationApplicationException;
import android.database.Cursor;
-import android.database.DatabaseUtils;
import android.net.Uri;
import android.os.RemoteException;
import android.support.v4.util.LongSparseArray;
-import org.spongycastle.bcpg.ArmoredOutputStream;
-import org.spongycastle.bcpg.S2K;
-import org.spongycastle.openpgp.PGPException;
-import org.spongycastle.openpgp.PGPKeyRing;
-import org.spongycastle.openpgp.PGPPublicKey;
-import org.spongycastle.openpgp.PGPPublicKeyRing;
-import org.spongycastle.openpgp.PGPSecretKey;
-import org.spongycastle.openpgp.PGPSecretKeyRing;
-import org.spongycastle.openpgp.PGPSignature;
-import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
-import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
+import org.sufficientlysecure.keychain.pgp.KeyRing;
+import org.sufficientlysecure.keychain.pgp.UncachedPublicKey;
+import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing;
+import org.sufficientlysecure.keychain.pgp.WrappedPublicKeyRing;
import org.sufficientlysecure.keychain.pgp.PgpHelper;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
+import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
+import org.sufficientlysecure.keychain.pgp.WrappedSignature;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps;
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData;
@@ -56,14 +50,12 @@ import org.sufficientlysecure.keychain.util.Log;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
-import java.security.SignatureException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
-import java.util.Map;
import java.util.Set;
public class ProviderHelper {
@@ -141,47 +133,29 @@ public class ProviderHelper {
public HashMap<String, Object> getUnifiedData(long masterKeyId, String[] proj, int[] types)
throws NotFoundException {
- return getGenericData(KeyRings.buildUnifiedKeyRingUri(Long.toString(masterKeyId)), proj, types);
+ return getGenericData(KeyRings.buildUnifiedKeyRingUri(masterKeyId), proj, types);
}
- /**
- * Find the master key id related to a given query. The id will either be extracted from the
- * query, which should work for all specific /key_rings/ queries, or will be queried if it can't.
- */
- public long extractOrGetMasterKeyId(Uri queryUri)
- throws NotFoundException {
- // try extracting from the uri first
- String firstSegment = queryUri.getPathSegments().get(1);
- if (!firstSegment.equals("find")) try {
- return Long.parseLong(firstSegment);
- } catch (NumberFormatException e) {
- // didn't work? oh well.
- Log.d(Constants.TAG, "Couldn't get masterKeyId from URI, querying...");
- }
- return getMasterKeyId(queryUri);
- }
-
- public long getMasterKeyId(Uri queryUri) throws NotFoundException {
- Object data = getGenericData(queryUri, KeyRings.MASTER_KEY_ID, FIELD_TYPE_INTEGER);
- if (data != null) {
- return (Long) data;
- } else {
- throw new NotFoundException();
- }
- }
-
- public LongSparseArray<PGPKeyRing> getPGPKeyRings(Uri queryUri) {
+ private LongSparseArray<UncachedPublicKey> getUncachedMasterKeys(Uri queryUri) {
Cursor cursor = mContentResolver.query(queryUri,
new String[]{KeyRingData.MASTER_KEY_ID, KeyRingData.KEY_RING_DATA},
null, null, null);
- LongSparseArray<PGPKeyRing> result = new LongSparseArray<PGPKeyRing>(cursor.getCount());
+ LongSparseArray<UncachedPublicKey> result =
+ new LongSparseArray<UncachedPublicKey>(cursor.getCount());
try {
if (cursor != null && cursor.moveToFirst()) do {
long masterKeyId = cursor.getLong(0);
byte[] data = cursor.getBlob(1);
if (data != null) {
- result.put(masterKeyId, PgpConversionHelper.BytesToPGPKeyRing(data));
+ try {
+ result.put(masterKeyId,
+ UncachedKeyRing.decodePublicFromData(data).getPublicKey());
+ } catch(PgpGeneralException e) {
+ Log.e(Constants.TAG, "Error parsing keyring, skipping.");
+ } catch(IOException e) {
+ Log.e(Constants.TAG, "IO error, skipping keyring");
+ }
}
} while (cursor.moveToNext());
} finally {
@@ -193,57 +167,74 @@ public class ProviderHelper {
return result;
}
- public PGPKeyRing getPGPKeyRing(Uri queryUri) throws NotFoundException {
- LongSparseArray<PGPKeyRing> result = getPGPKeyRings(queryUri);
- if (result.size() == 0) {
- throw new NotFoundException("PGPKeyRing object not found!");
- } else {
- return result.valueAt(0);
- }
+ public CachedPublicKeyRing getCachedPublicKeyRing(Uri queryUri) {
+ return new CachedPublicKeyRing(this, queryUri);
}
- public PGPPublicKeyRing getPGPPublicKeyRingWithKeyId(long keyId)
- throws NotFoundException {
- Uri uri = KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(Long.toString(keyId));
- long masterKeyId = getMasterKeyId(uri);
- return getPGPPublicKeyRing(masterKeyId);
+ public WrappedPublicKeyRing getWrappedPublicKeyRing(long id) throws NotFoundException {
+ return (WrappedPublicKeyRing) getWrappedKeyRing(KeyRings.buildUnifiedKeyRingUri(id), false);
}
- public PGPSecretKeyRing getPGPSecretKeyRingWithKeyId(long keyId)
- throws NotFoundException {
- Uri uri = KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(Long.toString(keyId));
- long masterKeyId = getMasterKeyId(uri);
- return getPGPSecretKeyRing(masterKeyId);
+ public WrappedPublicKeyRing getWrappedPublicKeyRing(Uri queryUri) throws NotFoundException {
+ return (WrappedPublicKeyRing) getWrappedKeyRing(queryUri, false);
}
- /**
- * Retrieves the actual PGPPublicKeyRing object from the database blob based on the masterKeyId
- */
- public PGPPublicKeyRing getPGPPublicKeyRing(long masterKeyId) throws NotFoundException {
- Uri queryUri = KeyRingData.buildPublicKeyRingUri(Long.toString(masterKeyId));
- return (PGPPublicKeyRing) getPGPKeyRing(queryUri);
+ public WrappedSecretKeyRing getWrappedSecretKeyRing(long id) throws NotFoundException {
+ return (WrappedSecretKeyRing) getWrappedKeyRing(KeyRings.buildUnifiedKeyRingUri(id), true);
}
- /**
- * Retrieves the actual PGPSecretKeyRing object from the database blob based on the maserKeyId
- */
- public PGPSecretKeyRing getPGPSecretKeyRing(long masterKeyId) throws NotFoundException {
- Uri queryUri = KeyRingData.buildSecretKeyRingUri(Long.toString(masterKeyId));
- return (PGPSecretKeyRing) getPGPKeyRing(queryUri);
+ public WrappedSecretKeyRing getWrappedSecretKeyRing(Uri queryUri) throws NotFoundException {
+ return (WrappedSecretKeyRing) getWrappedKeyRing(queryUri, true);
+ }
+
+ private KeyRing getWrappedKeyRing(Uri queryUri, boolean secret) throws NotFoundException {
+ Cursor cursor = mContentResolver.query(queryUri,
+ new String[]{
+ // we pick from cache only information that is not easily available from keyrings
+ KeyRings.HAS_ANY_SECRET, KeyRings.VERIFIED,
+ // and of course, ring data
+ secret ? KeyRings.PRIVKEY_DATA : KeyRings.PUBKEY_DATA
+ }, null, null, null
+ );
+ try {
+ if (cursor != null && cursor.moveToFirst()) {
+
+ boolean hasAnySecret = cursor.getInt(0) > 0;
+ int verified = cursor.getInt(1);
+ byte[] blob = cursor.getBlob(2);
+ if(secret &! hasAnySecret) {
+ throw new NotFoundException("Secret key not available!");
+ }
+ return secret
+ ? new WrappedSecretKeyRing(blob, hasAnySecret, verified)
+ : new WrappedPublicKeyRing(blob, hasAnySecret, verified);
+ } else {
+ throw new NotFoundException("Key not found!");
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
}
/**
* Saves PGPPublicKeyRing with its keys and userIds in DB
*/
@SuppressWarnings("unchecked")
- public void saveKeyRing(PGPPublicKeyRing keyRing) throws IOException {
- PGPPublicKey masterKey = keyRing.getPublicKey();
- long masterKeyId = masterKey.getKeyID();
+ public void savePublicKeyRing(UncachedKeyRing keyRing) throws IOException {
+ if (keyRing.isSecret()) {
+ throw new RuntimeException("Tried to save secret keyring as public! " +
+ "This is a bug, please file a bug report.");
+ }
+
+ UncachedPublicKey masterKey = keyRing.getPublicKey();
+ long masterKeyId = masterKey.getKeyId();
// IF there is a secret key, preserve it!
- PGPSecretKeyRing secretRing = null;
+ UncachedKeyRing secretRing = null;
try {
- secretRing = getPGPSecretKeyRing(masterKeyId);
+ secretRing = getWrappedSecretKeyRing(masterKeyId).getUncached();
} catch (NotFoundException e) {
Log.e(Constants.TAG, "key not found!");
}
@@ -266,36 +257,38 @@ public class ProviderHelper {
ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>();
int rank = 0;
- for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(keyRing.getPublicKeys())) {
+ for (UncachedPublicKey key : new IterableIterator<UncachedPublicKey>(keyRing.getPublicKeys())) {
operations.add(buildPublicKeyOperations(masterKeyId, key, rank));
++rank;
}
// get a list of owned secret keys, for verification filtering
- LongSparseArray<PGPKeyRing> allKeyRings = getPGPKeyRings(KeyRingData.buildSecretKeyRingUri());
+ LongSparseArray<UncachedPublicKey> allKeyRings =
+ getUncachedMasterKeys(KeyRingData.buildSecretKeyRingUri());
// special case: available secret keys verify themselves!
- if (secretRing != null)
- allKeyRings.put(secretRing.getSecretKey().getKeyID(), secretRing);
+ if (secretRing != null) {
+ allKeyRings.put(secretRing.getMasterKeyId(), secretRing.getPublicKey());
+ }
// classify and order user ids. primary are moved to the front, revoked to the back,
// otherwise the order in the keyfile is preserved.
List<UserIdItem> uids = new ArrayList<UserIdItem>();
- for (String userId : new IterableIterator<String>(masterKey.getUserIDs())) {
+ for (String userId : new IterableIterator<String>(
+ masterKey.getUnorderedUserIds().iterator())) {
UserIdItem item = new UserIdItem();
uids.add(item);
item.userId = userId;
// look through signatures for this specific key
- for (PGPSignature cert : new IterableIterator<PGPSignature>(
- masterKey.getSignaturesForID(userId))) {
- long certId = cert.getKeyID();
+ for (WrappedSignature cert : new IterableIterator<WrappedSignature>(
+ masterKey.getSignaturesForId(userId))) {
+ long certId = cert.getKeyId();
try {
// self signature
if (certId == masterKeyId) {
- cert.init(new JcaPGPContentVerifierBuilderProvider().setProvider(
- Constants.BOUNCY_CASTLE_PROVIDER_NAME), masterKey);
- if (!cert.verifyCertification(userId, masterKey)) {
+ cert.init(masterKey);
+ if (!cert.verifySignature(masterKey, userId)) {
// not verified?! dang! TODO notify user? this is kinda serious...
Log.e(Constants.TAG, "Could not verify self signature for " + userId + "!");
continue;
@@ -304,31 +297,22 @@ public class ProviderHelper {
if (item.selfCert == null ||
item.selfCert.getCreationTime().before(cert.getCreationTime())) {
item.selfCert = cert;
- item.isPrimary = cert.getHashedSubPackets().isPrimaryUserID();
- item.isRevoked =
- cert.getSignatureType() == PGPSignature.CERTIFICATION_REVOCATION;
+ item.isPrimary = cert.isPrimaryUserId();
+ item.isRevoked = cert.isRevocation();
}
}
// verify signatures from known private keys
if (allKeyRings.indexOfKey(certId) >= 0) {
- // mark them as verified
- cert.init(new JcaPGPContentVerifierBuilderProvider().setProvider(
- Constants.BOUNCY_CASTLE_PROVIDER_NAME),
- allKeyRings.get(certId).getPublicKey());
- if (cert.verifyCertification(userId, masterKey)) {
+ cert.init(allKeyRings.get(certId));
+ if (cert.verifySignature(masterKey, userId)) {
item.trustedCerts.add(cert);
}
}
- } catch (SignatureException e) {
+ } catch (PgpGeneralException e) {
Log.e(Constants.TAG, "Signature verification failed! "
- + PgpKeyHelper.convertKeyIdToHex(masterKey.getKeyID())
+ + PgpKeyHelper.convertKeyIdToHex(masterKey.getKeyId())
+ " from "
- + PgpKeyHelper.convertKeyIdToHex(cert.getKeyID()), e);
- } catch (PGPException e) {
- Log.e(Constants.TAG, "Signature verification failed! "
- + PgpKeyHelper.convertKeyIdToHex(masterKey.getKeyID())
- + " from "
- + PgpKeyHelper.convertKeyIdToHex(cert.getKeyID()), e);
+ + PgpKeyHelper.convertKeyIdToHex(cert.getKeyId()), e);
}
}
}
@@ -365,7 +349,7 @@ public class ProviderHelper {
// Save the saved keyring (if any)
if (secretRing != null) {
- saveKeyRing(secretRing);
+ saveSecretKeyRing(secretRing);
}
}
@@ -374,8 +358,8 @@ public class ProviderHelper {
String userId;
boolean isPrimary = false;
boolean isRevoked = false;
- PGPSignature selfCert;
- List<PGPSignature> trustedCerts = new ArrayList<PGPSignature>();
+ WrappedSignature selfCert;
+ List<WrappedSignature> trustedCerts = new ArrayList<WrappedSignature>();
@Override
public int compareTo(UserIdItem o) {
@@ -395,8 +379,13 @@ public class ProviderHelper {
* Saves a PGPSecretKeyRing in the DB. This will only work if a corresponding public keyring
* is already in the database!
*/
- public void saveKeyRing(PGPSecretKeyRing keyRing) throws IOException {
- long masterKeyId = keyRing.getPublicKey().getKeyID();
+ public void saveSecretKeyRing(UncachedKeyRing keyRing) throws IOException {
+ if (!keyRing.isSecret()) {
+ throw new RuntimeException("Tried to save publkc keyring as secret! " +
+ "This is a bug, please file a bug report.");
+ }
+
+ long masterKeyId = keyRing.getMasterKeyId();
{
Uri uri = Keys.buildKeysUri(Long.toString(masterKeyId));
@@ -408,14 +397,10 @@ public class ProviderHelper {
values.put(Keys.HAS_SECRET, 1);
// then, mark exactly the keys we have available
- for (PGPSecretKey sub : new IterableIterator<PGPSecretKey>(keyRing.getSecretKeys())) {
- S2K s2k = sub.getS2K();
- // Set to 1, except if the encryption type is GNU_DUMMY_S2K
- if(s2k == null || s2k.getType() != S2K.GNU_DUMMY_S2K) {
- mContentResolver.update(uri, values, Keys.KEY_ID + " = ?", new String[]{
- Long.toString(sub.getKeyID())
- });
- }
+ for (Long sub : new IterableIterator<Long>(keyRing.getAvailableSubkeys().iterator())) {
+ mContentResolver.update(uri, values, Keys.KEY_ID + " = ?", new String[] {
+ Long.toString(sub)
+ });
}
// this implicitly leaves all keys which were not in the secret key ring
// with has_secret = 0
@@ -436,39 +421,39 @@ public class ProviderHelper {
/**
* Saves (or updates) a pair of public and secret KeyRings in the database
*/
- public void saveKeyRing(PGPPublicKeyRing pubRing, PGPSecretKeyRing privRing) throws IOException {
- long masterKeyId = pubRing.getPublicKey().getKeyID();
+ public void saveKeyRing(UncachedKeyRing pubRing, UncachedKeyRing secRing) throws IOException {
+ long masterKeyId = pubRing.getPublicKey().getKeyId();
- // delete secret keyring (so it isn't unnecessarily saved by public-saveKeyRing below)
+ // delete secret keyring (so it isn't unnecessarily saved by public-savePublicKeyRing below)
mContentResolver.delete(KeyRingData.buildSecretKeyRingUri(Long.toString(masterKeyId)), null, null);
// save public keyring
- saveKeyRing(pubRing);
- saveKeyRing(privRing);
+ savePublicKeyRing(pubRing);
+ saveSecretKeyRing(secRing);
}
/**
* Build ContentProviderOperation to add PGPPublicKey to database corresponding to a keyRing
*/
private ContentProviderOperation
- buildPublicKeyOperations(long masterKeyId, PGPPublicKey key, int rank) throws IOException {
+ buildPublicKeyOperations(long masterKeyId, UncachedPublicKey key, int rank) throws IOException {
ContentValues values = new ContentValues();
values.put(Keys.MASTER_KEY_ID, masterKeyId);
values.put(Keys.RANK, rank);
- values.put(Keys.KEY_ID, key.getKeyID());
+ values.put(Keys.KEY_ID, key.getKeyId());
values.put(Keys.KEY_SIZE, key.getBitStrength());
values.put(Keys.ALGORITHM, key.getAlgorithm());
values.put(Keys.FINGERPRINT, key.getFingerprint());
- values.put(Keys.CAN_CERTIFY, (PgpKeyHelper.isCertificationKey(key)));
- values.put(Keys.CAN_SIGN, (PgpKeyHelper.isSigningKey(key)));
- values.put(Keys.CAN_ENCRYPT, PgpKeyHelper.isEncryptionKey(key));
- values.put(Keys.IS_REVOKED, key.isRevoked());
+ values.put(Keys.CAN_CERTIFY, key.canCertify());
+ values.put(Keys.CAN_SIGN, key.canSign());
+ values.put(Keys.CAN_ENCRYPT, key.canEncrypt());
+ values.put(Keys.IS_REVOKED, key.maybeRevoked());
- values.put(Keys.CREATION, PgpKeyHelper.getCreationDate(key).getTime() / 1000);
- Date expiryDate = PgpKeyHelper.getExpiryDate(key);
+ values.put(Keys.CREATION, key.getCreationTime().getTime() / 1000);
+ Date expiryDate = key.getExpiryTime();
if (expiryDate != null) {
values.put(Keys.EXPIRY, expiryDate.getTime() / 1000);
}
@@ -482,11 +467,12 @@ public class ProviderHelper {
* Build ContentProviderOperation to add PGPPublicKey to database corresponding to a keyRing
*/
private ContentProviderOperation
- buildCertOperations(long masterKeyId, int rank, PGPSignature cert, int verified) throws IOException {
+ buildCertOperations(long masterKeyId, int rank, WrappedSignature cert, int verified)
+ throws IOException {
ContentValues values = new ContentValues();
values.put(Certs.MASTER_KEY_ID, masterKeyId);
values.put(Certs.RANK, rank);
- values.put(Certs.KEY_ID_CERTIFIER, cert.getKeyID());
+ values.put(Certs.KEY_ID_CERTIFIER, cert.getKeyId());
values.put(Certs.TYPE, cert.getSignatureType());
values.put(Certs.CREATION, cert.getCreationTime().getTime() / 1000);
values.put(Certs.VERIFIED, verified);
@@ -514,23 +500,11 @@ public class ProviderHelper {
return ContentProviderOperation.newInsert(uri).withValues(values).build();
}
- private String getKeyRingAsArmoredString(byte[] data) throws IOException {
- Object keyRing = null;
- if (data != null) {
- keyRing = PgpConversionHelper.BytesToPGPKeyRing(data);
- }
+ private String getKeyRingAsArmoredString(byte[] data) throws IOException, PgpGeneralException {
+ UncachedKeyRing keyRing = UncachedKeyRing.decodeFromData(data);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
- ArmoredOutputStream aos = new ArmoredOutputStream(bos);
- aos.setHeader("Version", PgpHelper.getFullVersion(mContext));
-
- if (keyRing instanceof PGPSecretKeyRing) {
- aos.write(((PGPSecretKeyRing) keyRing).getEncoded());
- } else if (keyRing instanceof PGPPublicKeyRing) {
- aos.write(((PGPPublicKeyRing) keyRing).getEncoded());
- }
- aos.close();
-
+ keyRing.encodeArmored(bos, PgpHelper.getFullVersion(mContext));
String armoredKey = bos.toString("UTF-8");
Log.d(Constants.TAG, "armoredKey:" + armoredKey);
@@ -539,77 +513,12 @@ public class ProviderHelper {
}
public String getKeyRingAsArmoredString(Uri uri)
- throws NotFoundException, IOException {
+ throws NotFoundException, IOException, PgpGeneralException {
byte[] data = (byte[]) getGenericData(
uri, KeyRingData.KEY_RING_DATA, ProviderHelper.FIELD_TYPE_BLOB);
return getKeyRingAsArmoredString(data);
}
- /**
- * TODO: currently not used, but will be needed to upload many keys at once!
- *
- * @param masterKeyIds
- * @return
- * @throws IOException
- */
- public ArrayList<String> getKeyRingsAsArmoredString(long[] masterKeyIds)
- throws IOException {
- ArrayList<String> output = new ArrayList<String>();
-
- if (masterKeyIds == null || masterKeyIds.length == 0) {
- Log.e(Constants.TAG, "No master keys given!");
- return output;
- }
-
- // Build a cursor for the selected masterKeyIds
- Cursor cursor;
- {
- String inMasterKeyList = KeyRingData.MASTER_KEY_ID + " IN (";
- for (int i = 0; i < masterKeyIds.length; ++i) {
- if (i != 0) {
- inMasterKeyList += ", ";
- }
- inMasterKeyList += DatabaseUtils.sqlEscapeString("" + masterKeyIds[i]);
- }
- inMasterKeyList += ")";
-
- cursor = mContentResolver.query(KeyRingData.buildPublicKeyRingUri(), new String[]{
- KeyRingData._ID, KeyRingData.MASTER_KEY_ID, KeyRingData.KEY_RING_DATA
- }, inMasterKeyList, null, null);
- }
-
- try {
- if (cursor != null) {
- int masterIdCol = cursor.getColumnIndex(KeyRingData.MASTER_KEY_ID);
- int dataCol = cursor.getColumnIndex(KeyRingData.KEY_RING_DATA);
- if (cursor.moveToFirst()) {
- do {
- Log.d(Constants.TAG, "masterKeyId: " + cursor.getLong(masterIdCol));
-
- byte[] data = cursor.getBlob(dataCol);
-
- // get actual keyring data blob and write it to ByteArrayOutputStream
- try {
- output.add(getKeyRingAsArmoredString(data));
- } catch (IOException e) {
- Log.e(Constants.TAG, "IOException", e);
- }
- } while (cursor.moveToNext());
- }
- }
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
-
- if (output.size() > 0) {
- return output;
- } else {
- return null;
- }
- }
-
public ArrayList<String> getRegisteredApiApps() {
Cursor cursor = mContentResolver.query(ApiApps.CONTENT_URI, null, null, null, null);
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 db2db668d..17c277026 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
@@ -419,14 +419,14 @@ public class OpenPgpService extends RemoteService {
try {
// try to find key, throws NotFoundException if not in db!
- mProviderHelper.getPGPPublicKeyRing(masterKeyId);
+ mProviderHelper.getWrappedPublicKeyRing(masterKeyId);
Intent result = new Intent();
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
// also return PendingIntent that opens the key view activity
Intent intent = new Intent(getBaseContext(), ViewKeyActivity.class);
- intent.setData(KeyRings.buildGenericKeyRingUri(Long.toString(masterKeyId)));
+ intent.setData(KeyRings.buildGenericKeyRingUri(masterKeyId));
PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0,
intent,
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java
index 20223e319..cb58f8734 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java
@@ -33,6 +33,7 @@ import com.beardedhen.androidbootstrap.BootstrapButton;
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.remote.AccountSettings;
import org.sufficientlysecure.keychain.ui.EditKeyActivity;
@@ -179,9 +180,10 @@ public class AccountSettingsFragment extends Fragment implements
// select newly created key
try {
long masterKeyId = new ProviderHelper(getActivity())
- .extractOrGetMasterKeyId(data.getData());
+ .getCachedPublicKeyRing(data.getData())
+ .extractOrGetMasterKeyId();
mSelectKeyFragment.selectKey(masterKeyId);
- } catch (ProviderHelper.NotFoundException e) {
+ } catch (PgpGeneralException e) {
Log.e(Constants.TAG, "key not found!", e);
}
}
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 6f38418ff..38f40db29 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
@@ -26,29 +26,25 @@ import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
-import org.spongycastle.bcpg.sig.KeyFlags;
-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.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.FileHelper;
import org.sufficientlysecure.keychain.helper.OtherHelper;
import org.sufficientlysecure.keychain.helper.Preferences;
import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
-import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
+import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
+import org.sufficientlysecure.keychain.pgp.UncachedSecretKey;
+import org.sufficientlysecure.keychain.pgp.WrappedPublicKeyRing;
+import org.sufficientlysecure.keychain.pgp.WrappedSecretKey;
+import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult;
import org.sufficientlysecure.keychain.pgp.PgpHelper;
import org.sufficientlysecure.keychain.pgp.PgpImportExport;
-import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.pgp.PgpKeyOperation;
import org.sufficientlysecure.keychain.pgp.PgpSignEncrypt;
import org.sufficientlysecure.keychain.pgp.Progressable;
+import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
@@ -60,7 +56,6 @@ import org.sufficientlysecure.keychain.keyimport.KeybaseKeyserver;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ProgressScaler;
-import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
@@ -497,7 +492,7 @@ public class KeychainIntentService extends IntentService
} else if (ACTION_SAVE_KEYRING.equals(action)) {
try {
/* Input */
- SaveKeyringParcel saveParcel = data.getParcelable(SAVE_KEYRING_PARCEL);
+ OldSaveKeyringParcel saveParcel = data.getParcelable(SAVE_KEYRING_PARCEL);
String oldPassphrase = saveParcel.oldPassphrase;
String newPassphrase = saveParcel.newPassphrase;
boolean canSign = true;
@@ -510,33 +505,36 @@ public class KeychainIntentService extends IntentService
newPassphrase = oldPassphrase;
}
- long masterKeyId = saveParcel.keys.get(0).getKeyID();
+ long masterKeyId = saveParcel.keys.get(0).getKeyId();
/* Operation */
ProviderHelper providerHelper = new ProviderHelper(this);
if (!canSign) {
- PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 50, 100));
- PGPSecretKeyRing keyRing = providerHelper.getPGPSecretKeyRing(masterKeyId);
- keyRing = keyOperations.changeSecretKeyPassphrase(keyRing,
- oldPassphrase, newPassphrase);
+ setProgress(R.string.progress_building_key, 0, 100);
+ WrappedSecretKeyRing keyRing = providerHelper.getWrappedSecretKeyRing(masterKeyId);
+ UncachedKeyRing newKeyRing =
+ keyRing.changeSecretKeyPassphrase(oldPassphrase, newPassphrase);
setProgress(R.string.progress_saving_key_ring, 50, 100);
- providerHelper.saveKeyRing(keyRing);
+ providerHelper.saveSecretKeyRing(newKeyRing);
setProgress(R.string.progress_done, 100, 100);
} else {
PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 90, 100));
- PgpKeyOperation.Pair<PGPSecretKeyRing, PGPPublicKeyRing> pair;
try {
- PGPSecretKeyRing privkey = providerHelper.getPGPSecretKeyRing(masterKeyId);
- PGPPublicKeyRing pubkey = providerHelper.getPGPPublicKeyRing(masterKeyId);
+ WrappedSecretKeyRing seckey = providerHelper.getWrappedSecretKeyRing(masterKeyId);
+ WrappedPublicKeyRing pubkey = providerHelper.getWrappedPublicKeyRing(masterKeyId);
- pair = keyOperations.buildSecretKey(privkey, pubkey, saveParcel); // edit existing
+ PgpKeyOperation.Pair<UncachedKeyRing,UncachedKeyRing> pair =
+ keyOperations.buildSecretKey(seckey, pubkey, saveParcel); // edit existing
+ setProgress(R.string.progress_saving_key_ring, 90, 100);
+ providerHelper.saveKeyRing(pair.first, pair.second);
} catch (ProviderHelper.NotFoundException e) {
- pair = keyOperations.buildNewSecretKey(saveParcel); //new Keyring
+ PgpKeyOperation.Pair<UncachedKeyRing,UncachedKeyRing> pair =
+ keyOperations.buildNewSecretKey(saveParcel); //new Keyring
+ // save the pair
+ setProgress(R.string.progress_saving_key_ring, 90, 100);
+ providerHelper.saveKeyRing(pair.first, pair.second);
}
- setProgress(R.string.progress_saving_key_ring, 90, 100);
- // save the pair
- providerHelper.saveKeyRing(pair.second, pair.first);
setProgress(R.string.progress_done, 100, 100);
}
PassphraseCacheService.addCachedPassphrase(this, masterKeyId, newPassphrase);
@@ -556,13 +554,11 @@ public class KeychainIntentService extends IntentService
/* Operation */
PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 100, 100));
- PGPSecretKey newKey = keyOperations.createKey(algorithm, keysize,
- passphrase, masterKey);
+ byte[] newKey = keyOperations.createKey(algorithm, keysize, passphrase, masterKey);
/* Output */
Bundle resultData = new Bundle();
- resultData.putByteArray(RESULT_NEW_KEY,
- PgpConversionHelper.PGPSecretKeyToBytes(newKey));
+ resultData.putByteArray(RESULT_NEW_KEY, newKey);
OtherHelper.logDebugBundle(resultData, "resultData");
@@ -575,7 +571,6 @@ public class KeychainIntentService extends IntentService
try {
/* Input */
String passphrase = data.getString(GENERATE_KEY_SYMMETRIC_PASSPHRASE);
- ArrayList<PGPSecretKey> newKeys = new ArrayList<PGPSecretKey>();
ArrayList<Integer> keyUsageList = new ArrayList<Integer>();
/* Operation */
@@ -588,24 +583,28 @@ public class KeychainIntentService extends IntentService
keysTotal);
PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 100, 100));
- PGPSecretKey masterKey = keyOperations.createKey(Constants.choice.algorithm.rsa,
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+
+ byte[] buf;
+
+ buf = keyOperations.createKey(Constants.choice.algorithm.rsa,
4096, passphrase, true);
- newKeys.add(masterKey);
- keyUsageList.add(KeyFlags.CERTIFY_OTHER);
+ os.write(buf);
+ keyUsageList.add(UncachedSecretKey.CERTIFY_OTHER);
keysCreated++;
setProgress(keysCreated, keysTotal);
- PGPSecretKey subKey = keyOperations.createKey(Constants.choice.algorithm.rsa,
+ buf = keyOperations.createKey(Constants.choice.algorithm.rsa,
4096, passphrase, false);
- newKeys.add(subKey);
- keyUsageList.add(KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE);
+ os.write(buf);
+ keyUsageList.add(UncachedSecretKey.ENCRYPT_COMMS | UncachedSecretKey.ENCRYPT_STORAGE);
keysCreated++;
setProgress(keysCreated, keysTotal);
- subKey = keyOperations.createKey(Constants.choice.algorithm.rsa,
+ buf = keyOperations.createKey(Constants.choice.algorithm.rsa,
4096, passphrase, false);
- newKeys.add(subKey);
- keyUsageList.add(KeyFlags.SIGN_DATA);
+ os.write(buf);
+ keyUsageList.add(UncachedSecretKey.SIGN_DATA);
keysCreated++;
setProgress(keysCreated, keysTotal);
@@ -613,10 +612,8 @@ public class KeychainIntentService extends IntentService
// for sign
/* Output */
-
Bundle resultData = new Bundle();
- resultData.putByteArray(RESULT_NEW_KEY,
- PgpConversionHelper.PGPSecretKeyArrayListToBytes(newKeys));
+ resultData.putByteArray(RESULT_NEW_KEY, os.toByteArray());
resultData.putIntegerArrayList(RESULT_KEY_USAGES, keyUsageList);
OtherHelper.logDebugBundle(resultData, "resultData");
@@ -648,12 +645,10 @@ public class KeychainIntentService extends IntentService
}
} else if (ACTION_IMPORT_KEYRING.equals(action)) {
try {
- List<ImportKeysListEntry> entries = data.getParcelableArrayList(IMPORT_KEY_LIST);
-
- Bundle resultData = new Bundle();
+ List<ParcelableKeyRing> entries = data.getParcelableArrayList(IMPORT_KEY_LIST);
PgpImportExport pgpImportExport = new PgpImportExport(this, this);
- resultData = pgpImportExport.importKeyRings(entries);
+ Bundle resultData = pgpImportExport.importKeyRings(entries);
sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData);
} catch (Exception e) {
@@ -727,15 +722,12 @@ public class KeychainIntentService extends IntentService
HkpKeyserver server = new HkpKeyserver(keyServer);
ProviderHelper providerHelper = new ProviderHelper(this);
- PGPPublicKeyRing keyring = (PGPPublicKeyRing) providerHelper.getPGPKeyRing(dataUri);
- if (keyring != null) {
- PgpImportExport pgpImportExport = new PgpImportExport(this, null);
-
- boolean uploaded = pgpImportExport.uploadKeyRingToServer(server,
- (PGPPublicKeyRing) keyring);
- if (!uploaded) {
- throw new PgpGeneralException("Unable to export key to selected server");
- }
+ WrappedPublicKeyRing keyring = providerHelper.getWrappedPublicKeyRing(dataUri);
+ PgpImportExport pgpImportExport = new PgpImportExport(this, null);
+
+ boolean uploaded = pgpImportExport.uploadKeyRingToServer(server, keyring);
+ if (!uploaded) {
+ throw new PgpGeneralException("Unable to export key to selected server");
}
sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY);
@@ -747,40 +739,21 @@ public class KeychainIntentService extends IntentService
try {
KeybaseKeyserver server = new KeybaseKeyserver();
+ ArrayList<ParcelableKeyRing> keyRings = new ArrayList<ParcelableKeyRing>(entries.size());
for (ImportKeysListEntry entry : entries) {
// the keybase handle is in userId(1)
String keybaseId = entry.getExtraData();
byte[] downloadedKeyBytes = server.get(keybaseId).getBytes();
- // create PGPKeyRing object based on downloaded armored key
- PGPKeyRing downloadedKey = null;
- BufferedInputStream bufferedInput =
- new BufferedInputStream(new ByteArrayInputStream(downloadedKeyBytes));
- if (bufferedInput.available() > 0) {
- InputStream in = PGPUtil.getDecoderStream(bufferedInput);
- PGPObjectFactory objectFactory = new PGPObjectFactory(in);
-
- // get first object in block
- Object obj;
- if ((obj = objectFactory.nextObject()) != null) {
-
- if (obj instanceof PGPKeyRing) {
- downloadedKey = (PGPKeyRing) obj;
- } else {
- throw new PgpGeneralException("Object not recognized as PGPKeyRing!");
- }
- }
- }
-
// save key bytes in entry object for doing the
// actual import afterwards
- entry.setBytes(downloadedKey.getEncoded());
+ keyRings.add(new ParcelableKeyRing(downloadedKeyBytes));
}
Intent importIntent = new Intent(this, KeychainIntentService.class);
importIntent.setAction(ACTION_IMPORT_KEYRING);
Bundle importData = new Bundle();
- importData.putParcelableArrayList(IMPORT_KEY_LIST, entries);
+ importData.putParcelableArrayList(IMPORT_KEY_LIST, keyRings);
importIntent.putExtra(EXTRA_DATA, importData);
importIntent.putExtra(EXTRA_MESSENGER, mMessenger);
@@ -794,11 +767,12 @@ public class KeychainIntentService extends IntentService
} else if (ACTION_DOWNLOAD_AND_IMPORT_KEYS.equals(action)) {
try {
ArrayList<ImportKeysListEntry> entries = data.getParcelableArrayList(DOWNLOAD_KEY_LIST);
- String keyServer = data.getString(DOWNLOAD_KEY_SERVER);
// this downloads the keys and places them into the ImportKeysListEntry entries
+ String keyServer = data.getString(DOWNLOAD_KEY_SERVER);
HkpKeyserver server = new HkpKeyserver(keyServer);
+ ArrayList<ParcelableKeyRing> keyRings = new ArrayList<ParcelableKeyRing>(entries.size());
for (ImportKeysListEntry entry : entries) {
// if available use complete fingerprint for get request
byte[] downloadedKeyBytes;
@@ -808,49 +782,15 @@ public class KeychainIntentService extends IntentService
downloadedKeyBytes = server.get(entry.getKeyIdHex()).getBytes();
}
- // create PGPKeyRing object based on downloaded armored key
- PGPKeyRing downloadedKey = null;
- BufferedInputStream bufferedInput =
- new BufferedInputStream(new ByteArrayInputStream(downloadedKeyBytes));
- if (bufferedInput.available() > 0) {
- InputStream in = PGPUtil.getDecoderStream(bufferedInput);
- PGPObjectFactory objectFactory = new PGPObjectFactory(in);
-
- // get first object in block
- Object obj;
- if ((obj = objectFactory.nextObject()) != null) {
-
- if (obj instanceof PGPKeyRing) {
- downloadedKey = (PGPKeyRing) obj;
- } else {
- throw new PgpGeneralException("Object not recognized as PGPKeyRing!");
- }
- }
- }
-
- // verify downloaded key by comparing fingerprints
- if (entry.getFingerprintHex() != null) {
- String downloadedKeyFp = PgpKeyHelper.convertFingerprintToHex(
- downloadedKey.getPublicKey().getFingerprint());
- if (downloadedKeyFp.equals(entry.getFingerprintHex())) {
- Log.d(Constants.TAG, "fingerprint of downloaded key is the same as " +
- "the requested fingerprint!");
- } else {
- throw new PgpGeneralException("fingerprint of downloaded key is " +
- "NOT the same as the requested fingerprint!");
- }
- }
-
// save key bytes in entry object for doing the
// actual import afterwards
- entry.setBytes(downloadedKey.getEncoded());
+ keyRings.add(new ParcelableKeyRing(downloadedKeyBytes, entry.getFingerprintHex()));
}
-
Intent importIntent = new Intent(this, KeychainIntentService.class);
importIntent.setAction(ACTION_IMPORT_KEYRING);
Bundle importData = new Bundle();
- importData.putParcelableArrayList(IMPORT_KEY_LIST, entries);
+ importData.putParcelableArrayList(IMPORT_KEY_LIST, keyRings);
importIntent.putExtra(EXTRA_DATA, importData);
importIntent.putExtra(EXTRA_MESSENGER, mMessenger);
@@ -877,29 +817,18 @@ public class KeychainIntentService extends IntentService
}
ProviderHelper providerHelper = new ProviderHelper(this);
- PgpKeyOperation keyOperation = new PgpKeyOperation(new ProgressScaler(this, 0, 100, 100));
- PGPPublicKeyRing publicRing = providerHelper.getPGPPublicKeyRing(pubKeyId);
- PGPPublicKey publicKey = publicRing.getPublicKey(pubKeyId);
- PGPSecretKeyRing secretKeyRing = null;
- try {
- secretKeyRing = providerHelper.getPGPSecretKeyRing(masterKeyId);
- } catch (ProviderHelper.NotFoundException e) {
- Log.e(Constants.TAG, "key not found!", e);
- // TODO: throw exception here!
+ WrappedPublicKeyRing publicRing = providerHelper.getWrappedPublicKeyRing(pubKeyId);
+ WrappedSecretKeyRing secretKeyRing = providerHelper.getWrappedSecretKeyRing(masterKeyId);
+ WrappedSecretKey certificationKey = secretKeyRing.getSubKey();
+ if(!certificationKey.unlock(signaturePassphrase)) {
+ throw new PgpGeneralException("Error extracting key (bad passphrase?)");
}
- PGPSecretKey certificationKey = PgpKeyHelper.getFirstCertificationSubkey(secretKeyRing);
- publicKey = keyOperation.certifyKey(certificationKey, publicKey,
- userIds, signaturePassphrase);
- publicRing = PGPPublicKeyRing.insertPublicKey(publicRing, publicKey);
+ UncachedKeyRing newRing = certificationKey.certifyUserIds(publicRing, userIds);
// store the signed key in our local cache
- PgpImportExport pgpImportExport = new PgpImportExport(this, null);
- int retval = pgpImportExport.storeKeyRingInCache(publicRing);
- if (retval != PgpImportExport.RETURN_OK && retval != PgpImportExport.RETURN_UPDATED) {
- throw new PgpGeneralException("Failed to store signed key in local cache");
- }
-
+ providerHelper.savePublicKeyRing(newRing);
sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY);
+
} catch (Exception e) {
sendErrorToHandler(e);
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OldSaveKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OldSaveKeyringParcel.java
new file mode 100644
index 000000000..b722393ad
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OldSaveKeyringParcel.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2014 Ash Hughes <ashes-iontach@hotmail.com>
+ *
+ * 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.service;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
+import org.sufficientlysecure.keychain.pgp.UncachedSecretKey;
+import org.sufficientlysecure.keychain.util.IterableIterator;
+import org.sufficientlysecure.keychain.util.Log;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Calendar;
+
+/** Class for parcelling data between ui and services.
+ * This class is outdated and scheduled for removal, pending a rewrite of the
+ * EditKeyActivity and save keyring routines.
+ */
+@Deprecated
+public class OldSaveKeyringParcel implements Parcelable {
+
+ public ArrayList<String> userIds;
+ public ArrayList<String> originalIDs;
+ public ArrayList<String> deletedIDs;
+ public boolean[] newIDs;
+ public boolean primaryIDChanged;
+ public boolean[] moddedKeys;
+ public ArrayList<UncachedSecretKey> deletedKeys;
+ public ArrayList<Calendar> keysExpiryDates;
+ public ArrayList<Integer> keysUsages;
+ public String newPassphrase;
+ public String oldPassphrase;
+ public boolean[] newKeys;
+ public ArrayList<UncachedSecretKey> keys;
+ public String originalPrimaryID;
+
+ public OldSaveKeyringParcel() {}
+
+ private OldSaveKeyringParcel(Parcel source) {
+ userIds = (ArrayList<String>) source.readSerializable();
+ originalIDs = (ArrayList<String>) source.readSerializable();
+ deletedIDs = (ArrayList<String>) source.readSerializable();
+ newIDs = source.createBooleanArray();
+ primaryIDChanged = source.readByte() != 0;
+ moddedKeys = source.createBooleanArray();
+ byte[] tmp = source.createByteArray();
+ if (tmp == null) {
+ deletedKeys = null;
+ } else {
+ deletedKeys = PgpConversionHelper.BytesToPGPSecretKeyList(tmp);
+ }
+ keysExpiryDates = (ArrayList<Calendar>) source.readSerializable();
+ keysUsages = source.readArrayList(Integer.class.getClassLoader());
+ newPassphrase = source.readString();
+ oldPassphrase = source.readString();
+ newKeys = source.createBooleanArray();
+ keys = PgpConversionHelper.BytesToPGPSecretKeyList(source.createByteArray());
+ originalPrimaryID = source.readString();
+ }
+
+ @Override
+ public void writeToParcel(Parcel destination, int flags) {
+ destination.writeSerializable(userIds); //might not be the best method to store.
+ destination.writeSerializable(originalIDs);
+ destination.writeSerializable(deletedIDs);
+ destination.writeBooleanArray(newIDs);
+ destination.writeByte((byte) (primaryIDChanged ? 1 : 0));
+ destination.writeBooleanArray(moddedKeys);
+ destination.writeByteArray(encodeArrayList(deletedKeys));
+ destination.writeSerializable(keysExpiryDates);
+ destination.writeList(keysUsages);
+ destination.writeString(newPassphrase);
+ destination.writeString(oldPassphrase);
+ destination.writeBooleanArray(newKeys);
+ destination.writeByteArray(encodeArrayList(keys));
+ destination.writeString(originalPrimaryID);
+ }
+
+ public static final Creator<OldSaveKeyringParcel> CREATOR = new Creator<OldSaveKeyringParcel>() {
+ public OldSaveKeyringParcel createFromParcel(final Parcel source) {
+ return new OldSaveKeyringParcel(source);
+ }
+
+ public OldSaveKeyringParcel[] newArray(final int size) {
+ return new OldSaveKeyringParcel[size];
+ }
+ };
+
+ private static byte[] encodeArrayList(ArrayList<UncachedSecretKey> list) {
+ if(list.isEmpty()) {
+ return null;
+ }
+
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ for(UncachedSecretKey key : new IterableIterator<UncachedSecretKey>(list.iterator())) {
+ try {
+ key.encodeSecretKey(os);
+ } catch (IOException e) {
+ Log.e(Constants.TAG, "Error while converting ArrayList<UncachedSecretKey> to byte[]!", e);
+ }
+ }
+ return os.toByteArray();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+}
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 db4fecef0..d42bae67a 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java
@@ -34,20 +34,14 @@ import android.os.Messenger;
import android.os.RemoteException;
import android.support.v4.util.LongSparseArray;
-import org.spongycastle.openpgp.PGPException;
-import org.spongycastle.openpgp.PGPPrivateKey;
-import org.spongycastle.openpgp.PGPSecretKey;
-import org.spongycastle.openpgp.PGPSecretKeyRing;
-import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
-import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.helper.Preferences;
+import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.util.Log;
import java.util.Date;
-import java.util.Iterator;
/**
* This service runs in its own process, but is available to all other processes as the main
@@ -163,81 +157,47 @@ public class PassphraseCacheService extends Service {
* @return
*/
private String getCachedPassphraseImpl(long keyId) {
- Log.d(TAG, "getCachedPassphraseImpl() get masterKeyId for " + keyId);
-
- // try to get master key id which is used as an identifier for cached passphrases
- long masterKeyId = keyId;
- if (masterKeyId != Constants.key.symmetric) {
- try {
- masterKeyId = new ProviderHelper(this).getMasterKeyId(
- KeychainContract.KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(Long.toString(keyId)));
- } catch (ProviderHelper.NotFoundException e) {
+ // passphrase for symmetric encryption?
+ if (keyId == Constants.key.symmetric) {
+ Log.d(TAG, "getCachedPassphraseImpl() for symmetric encryption");
+ String cachedPassphrase = mPassphraseCache.get(Constants.key.symmetric);
+ if (cachedPassphrase == null) {
return null;
}
+ addCachedPassphrase(this, Constants.key.symmetric, cachedPassphrase);
+ return cachedPassphrase;
}
- Log.d(TAG, "getCachedPassphraseImpl() for masterKeyId " + masterKeyId);
- // get cached passphrase
- String cachedPassphrase = mPassphraseCache.get(masterKeyId);
- if (cachedPassphrase == null) {
- // if key has no passphrase -> cache and return empty passphrase
- if (!hasPassphrase(this, masterKeyId)) {
+ // try to get master key id which is used as an identifier for cached passphrases
+ try {
+ Log.d(TAG, "getCachedPassphraseImpl() for masterKeyId " + keyId);
+ WrappedSecretKeyRing key = new ProviderHelper(this).getWrappedSecretKeyRing(
+ 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!");
- addCachedPassphrase(this, masterKeyId, "");
+ addCachedPassphrase(this, keyId, "");
return "";
- } else {
- return null;
}
- }
- // set it again to reset the cache life cycle
- Log.d(TAG, "Cache passphrase again when getting it!");
- addCachedPassphrase(this, masterKeyId, cachedPassphrase);
- return cachedPassphrase;
- }
-
- public static boolean hasPassphrase(PGPSecretKeyRing secretKeyRing) {
- PGPSecretKey secretKey = null;
- boolean foundValidKey = false;
- for (Iterator keys = secretKeyRing.getSecretKeys(); keys.hasNext(); ) {
- secretKey = (PGPSecretKey) keys.next();
- if (!secretKey.isPrivateKeyEmpty()) {
- foundValidKey = true;
- break;
+ // get cached passphrase
+ String cachedPassphrase = mPassphraseCache.get(keyId);
+ if (cachedPassphrase == null) {
+ Log.d(TAG, "Passphrase not (yet) cached, returning null");
+ // not really an error, just means the passphrase is not cached but not empty either
+ return null;
}
- }
- if(!foundValidKey) {
- return false;
- }
- try {
- PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
- .setProvider("SC").build("".toCharArray());
- PGPPrivateKey testKey = secretKey.extractPrivateKey(keyDecryptor);
- return testKey == null;
- } catch(PGPException e) {
- // this means the crc check failed -> passphrase required
- return true;
- }
- }
+ // set it again to reset the cache life cycle
+ Log.d(TAG, "Cache passphrase again when getting it!");
+ addCachedPassphrase(this, keyId, cachedPassphrase);
+ return cachedPassphrase;
- /**
- * Checks if key has a passphrase.
- *
- * @param secretKeyId
- * @return true if it has a passphrase
- */
- public static boolean hasPassphrase(Context context, long secretKeyId) {
- // check if the key has no passphrase
- try {
- PGPSecretKeyRing secRing = new ProviderHelper(context).getPGPSecretKeyRing(secretKeyId);
- return hasPassphrase(secRing);
} catch (ProviderHelper.NotFoundException e) {
- Log.e(Constants.TAG, "key not found!", e);
+ Log.e(TAG, "Passphrase for unknown key was requested!");
+ return null;
}
-
- return true;
}
/**
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java
index 3f0b37b75..3514ab2e5 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java
@@ -1,92 +1,101 @@
-/*
- * Copyright (C) 2014 Ash Hughes <ashes-iontach@hotmail.com>
+package org.sufficientlysecure.keychain.service;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.io.Serializable;
+import java.util.HashMap;
+
+/** This class is a a transferable representation for a collection of changes
+ * to be done on a keyring.
*
- * 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 class should include all types of operations supported in the backend.
*
- * 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.
+ * All changes are done in a differential manner. Besides the two key
+ * identification attributes, all attributes may be null, which indicates no
+ * change to the keyring. This is also the reason why boxed values are used
+ * instead of primitives in the subclasses.
*
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * Application of operations in the backend should be fail-fast, which means an
+ * error in any included operation (for example revocation of a non-existent
+ * subkey) will cause the operation as a whole to fail.
*/
+public class SaveKeyringParcel implements Parcelable {
-package org.sufficientlysecure.keychain.service;
+ // the master key id to be edited
+ private final long mMasterKeyId;
+ // the key fingerprint, for safety
+ private final byte[] mFingerprint;
-import android.os.Parcel;
-import android.os.Parcelable;
+ public String newPassphrase;
-import org.spongycastle.openpgp.PGPSecretKey;
-import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
+ public String[] addUserIds;
+ public SubkeyAdd[] addSubKeys;
-import java.util.ArrayList;
-import java.util.Calendar;
+ public HashMap<Long, SubkeyChange> changeSubKeys;
+ public String changePrimaryUserId;
-public class SaveKeyringParcel implements Parcelable {
+ public String[] revokeUserIds;
+ public long[] revokeSubKeys;
- public ArrayList<String> userIds;
- public ArrayList<String> originalIDs;
- public ArrayList<String> deletedIDs;
- public boolean[] newIDs;
- public boolean primaryIDChanged;
- public boolean[] moddedKeys;
- public ArrayList<PGPSecretKey> deletedKeys;
- public ArrayList<Calendar> keysExpiryDates;
- public ArrayList<Integer> keysUsages;
- public String newPassphrase;
- public String oldPassphrase;
- public boolean[] newKeys;
- public ArrayList<PGPSecretKey> keys;
- public String originalPrimaryID;
-
- public SaveKeyringParcel() {}
-
- private SaveKeyringParcel(Parcel source) {
- userIds = (ArrayList<String>) source.readSerializable();
- originalIDs = (ArrayList<String>) source.readSerializable();
- deletedIDs = (ArrayList<String>) source.readSerializable();
- newIDs = source.createBooleanArray();
- primaryIDChanged = source.readByte() != 0;
- moddedKeys = source.createBooleanArray();
- byte[] tmp = source.createByteArray();
- if (tmp == null) {
- deletedKeys = null;
- } else {
- deletedKeys = PgpConversionHelper.BytesToPGPSecretKeyList(tmp);
+ public SaveKeyringParcel(long masterKeyId, byte[] fingerprint) {
+ mMasterKeyId = masterKeyId;
+ mFingerprint = fingerprint;
+ }
+
+ // performance gain for using Parcelable here would probably be negligible,
+ // use Serializable instead.
+ public static class SubkeyAdd implements Serializable {
+ public final int mAlgorithm;
+ public final int mKeysize;
+ public final int mFlags;
+ public final Long mExpiry;
+ public SubkeyAdd(int algorithm, int keysize, int flags, Long expiry) {
+ mAlgorithm = algorithm;
+ mKeysize = keysize;
+ mFlags = flags;
+ mExpiry = expiry;
+ }
+ }
+
+ public static class SubkeyChange implements Serializable {
+ public final long mKeyId;
+ public final Integer mFlags;
+ public final Long mExpiry;
+ public SubkeyChange(long keyId, Integer flags, Long expiry) {
+ mKeyId = keyId;
+ mFlags = flags;
+ mExpiry = expiry;
}
- keysExpiryDates = (ArrayList<Calendar>) source.readSerializable();
- keysUsages = source.readArrayList(Integer.class.getClassLoader());
- newPassphrase = source.readString();
- oldPassphrase = source.readString();
- newKeys = source.createBooleanArray();
- keys = PgpConversionHelper.BytesToPGPSecretKeyList(source.createByteArray());
- originalPrimaryID = source.readString();
+ }
+
+ public SaveKeyringParcel(Parcel source) {
+ mMasterKeyId = source.readLong();
+ mFingerprint = source.createByteArray();
+
+ addUserIds = source.createStringArray();
+ addSubKeys = (SubkeyAdd[]) source.readSerializable();
+
+ changeSubKeys = (HashMap<Long,SubkeyChange>) source.readSerializable();
+ changePrimaryUserId = source.readString();
+
+ revokeUserIds = source.createStringArray();
+ revokeSubKeys = source.createLongArray();
}
@Override
public void writeToParcel(Parcel destination, int flags) {
- destination.writeSerializable(userIds); //might not be the best method to store.
- destination.writeSerializable(originalIDs);
- destination.writeSerializable(deletedIDs);
- destination.writeBooleanArray(newIDs);
- destination.writeByte((byte) (primaryIDChanged ? 1 : 0));
- destination.writeBooleanArray(moddedKeys);
- byte[] tmp = null;
- if (deletedKeys.size() != 0) {
- tmp = PgpConversionHelper.PGPSecretKeyArrayListToBytes(deletedKeys);
- }
- destination.writeByteArray(tmp);
- destination.writeSerializable(keysExpiryDates);
- destination.writeList(keysUsages);
- destination.writeString(newPassphrase);
- destination.writeString(oldPassphrase);
- destination.writeBooleanArray(newKeys);
- destination.writeByteArray(PgpConversionHelper.PGPSecretKeyArrayListToBytes(keys));
- destination.writeString(originalPrimaryID);
+ destination.writeLong(mMasterKeyId);
+ destination.writeByteArray(mFingerprint);
+
+ destination.writeStringArray(addUserIds);
+ destination.writeSerializable(addSubKeys);
+
+ destination.writeSerializable(changeSubKeys);
+ destination.writeString(changePrimaryUserId);
+
+ destination.writeStringArray(revokeUserIds);
+ destination.writeLongArray(revokeSubKeys);
}
public static final Creator<SaveKeyringParcel> CREATOR = new Creator<SaveKeyringParcel>() {
@@ -103,4 +112,5 @@ public class SaveKeyringParcel implements Parcelable {
public int describeContents() {
return 0;
}
+
}
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 bd12a3b52..6c74818a5 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java
@@ -43,7 +43,6 @@ import android.widget.TextView;
import com.devspark.appmsg.AppMsg;
-import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.Preferences;
@@ -51,7 +50,6 @@ import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
@@ -222,54 +220,22 @@ public class CertifyKeyActivity extends ActionBarActivity implements
* handles the UI bits of the signing process on the UI thread
*/
private void initiateSigning() {
- try {
- PGPPublicKeyRing pubring = new ProviderHelper(this).getPGPPublicKeyRing(mPubKeyId);
-
- // if we have already signed this key, dont bother doing it again
- boolean alreadySigned = false;
-
- /* todo: reconsider this at a later point when certs are in the db
- @SuppressWarnings("unchecked")
- Iterator<PGPSignature> itr = pubring.getPublicKey(mPubKeyId).getSignatures();
- while (itr.hasNext()) {
- PGPSignature sig = itr.next();
- if (sig.getKeyID() == mMasterKeyId) {
- alreadySigned = true;
- break;
- }
- }
- */
-
- if (!alreadySigned) {
- /*
- * get the user's passphrase for this key (if required)
- */
- String passphrase = PassphraseCacheService.getCachedPassphrase(this, mMasterKeyId);
- if (passphrase == null) {
- PassphraseDialogFragment.show(this, mMasterKeyId,
- new Handler() {
- @Override
- public void handleMessage(Message message) {
- if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
- startSigning();
- }
- }
+ // get the user's passphrase for this key (if required)
+ String passphrase = PassphraseCacheService.getCachedPassphrase(this, mMasterKeyId);
+ if (passphrase == null) {
+ PassphraseDialogFragment.show(this, mMasterKeyId,
+ new Handler() {
+ @Override
+ public void handleMessage(Message message) {
+ if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
+ startSigning();
}
- );
- // bail out; need to wait until the user has entered the passphrase before trying again
- return;
- } else {
- startSigning();
- }
- } else {
- AppMsg.makeText(this, R.string.key_has_already_been_certified, AppMsg.STYLE_ALERT)
- .show();
-
- setResult(RESULT_CANCELED);
- finish();
- }
- } catch (ProviderHelper.NotFoundException e) {
- Log.e(Constants.TAG, "key not found!", e);
+ }
+ });
+ // bail out; need to wait until the user has entered the passphrase before trying again
+ return;
+ } else {
+ startSigning();
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java
index bd3a98567..d9c7a1736 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java
@@ -19,7 +19,6 @@
package org.sufficientlysecure.keychain.ui;
import android.app.Activity;
-import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
@@ -44,21 +43,22 @@ import android.widget.Toast;
import com.beardedhen.androidbootstrap.BootstrapButton;
import com.devspark.appmsg.AppMsg;
-import org.spongycastle.openpgp.PGPSecretKey;
-import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.ActionBarHelper;
import org.sufficientlysecure.keychain.helper.ExportHelper;
+import org.sufficientlysecure.keychain.pgp.WrappedSecretKey;
+import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing;
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
+import org.sufficientlysecure.keychain.pgp.UncachedSecretKey;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
-import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
+import org.sufficientlysecure.keychain.service.OldSaveKeyringParcel;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
-import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.ui.dialog.CustomAlertDialogBuilder;
import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.SetPassphraseDialogFragment;
@@ -67,7 +67,6 @@ import org.sufficientlysecure.keychain.ui.widget.Editor.EditorListener;
import org.sufficientlysecure.keychain.ui.widget.KeyEditor;
import org.sufficientlysecure.keychain.ui.widget.SectionView;
import org.sufficientlysecure.keychain.ui.widget.UserIdEditor;
-import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
import java.util.ArrayList;
@@ -89,8 +88,6 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
// EDIT
private Uri mDataUri;
- private PGPSecretKeyRing mKeyRing = null;
-
private SectionView mUserIdsView;
private SectionView mKeysView;
@@ -106,7 +103,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
private CheckBox mNoPassphrase;
Vector<String> mUserIds;
- Vector<PGPSecretKey> mKeys;
+ Vector<UncachedSecretKey> mKeys;
Vector<Integer> mKeysUsages;
boolean mMasterCanSign = true;
@@ -159,7 +156,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
);
mUserIds = new Vector<String>();
- mKeys = new Vector<PGPSecretKey>();
+ mKeys = new Vector<UncachedSecretKey>();
mKeysUsages = new Vector<Integer>();
// Catch Intents opened from other apps
@@ -240,7 +237,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
// get new key from data bundle returned from service
Bundle data = message.getData();
- ArrayList<PGPSecretKey> newKeys =
+ ArrayList<UncachedSecretKey> newKeys =
PgpConversionHelper.BytesToPGPSecretKeyList(data
.getByteArray(KeychainIntentService.RESULT_NEW_KEY));
@@ -288,18 +285,18 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
Log.d(Constants.TAG, "uri: " + mDataUri);
try {
- Uri secretUri = KeychainContract.KeyRingData.buildSecretKeyRingUri(mDataUri);
- mKeyRing = (PGPSecretKeyRing) new ProviderHelper(this).getPGPKeyRing(secretUri);
-
- PGPSecretKey masterKey = mKeyRing.getSecretKey();
- mMasterCanSign = PgpKeyHelper.isCertificationKey(mKeyRing.getSecretKey());
- for (PGPSecretKey key : new IterableIterator<PGPSecretKey>(mKeyRing.getSecretKeys())) {
- mKeys.add(key);
- mKeysUsages.add(-1); // get usage when view is created
+ Uri secretUri = KeyRings.buildUnifiedKeyRingUri(mDataUri);
+ WrappedSecretKeyRing keyRing = new ProviderHelper(this).getWrappedSecretKeyRing(secretUri);
+
+ mMasterCanSign = keyRing.getSubKey().canCertify();
+ for (WrappedSecretKey key : keyRing.iterator()) {
+ // Turn into uncached instance
+ mKeys.add(key.getUncached());
+ mKeysUsages.add(key.getKeyUsage()); // get usage when view is created
}
boolean isSet = false;
- for (String userId : new IterableIterator<String>(masterKey.getUserIDs())) {
+ for (String userId : keyRing.getSubKey().getUserIds()) {
Log.d(Constants.TAG, "Added userId " + userId);
if (!isSet) {
isSet = true;
@@ -314,7 +311,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
buildLayout(false);
mCurrentPassphrase = "";
- mIsPassphraseSet = PassphraseCacheService.hasPassphrase(mKeyRing);
+ mIsPassphraseSet = keyRing.hasPassphrase();
if (!mIsPassphraseSet) {
// check "no passphrase" checkbox and remove button
mNoPassphrase.setChecked(true);
@@ -432,7 +429,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
if (mKeysView.getEditors().getChildCount() == 0) {
return 0;
}
- return ((KeyEditor) mKeysView.getEditors().getChildAt(0)).getValue().getKeyID();
+ return ((KeyEditor) mKeysView.getEditors().getChildAt(0)).getValue().getKeyId();
}
public boolean isPassphraseSet() {
@@ -556,7 +553,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
intent.setAction(KeychainIntentService.ACTION_SAVE_KEYRING);
- SaveKeyringParcel saveParams = new SaveKeyringParcel();
+ OldSaveKeyringParcel saveParams = new OldSaveKeyringParcel();
saveParams.userIds = getUserIds(mUserIdsView);
saveParams.originalIDs = mUserIdsView.getOriginalIDs();
saveParams.deletedIDs = mUserIdsView.getDeletedIDs();
@@ -572,7 +569,6 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
saveParams.keys = getKeys(mKeysView);
saveParams.originalPrimaryID = mUserIdsView.getOriginalPrimaryID();
-
// fill values for this action
Bundle data = new Bundle();
data.putBoolean(KeychainIntentService.SAVE_KEYRING_CAN_SIGN, mMasterCanSign);
@@ -591,8 +587,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
Intent data = new Intent();
// return uri pointing to new created key
- Uri uri = KeychainContract.KeyRings.buildGenericKeyRingUri(
- String.valueOf(getMasterKeyId()));
+ Uri uri = KeyRings.buildGenericKeyRingUri(getMasterKeyId());
data.setData(uri);
setResult(RESULT_OK, data);
@@ -690,8 +685,8 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
* @param keysView
* @return
*/
- private ArrayList<PGPSecretKey> getKeys(SectionView keysView) throws PgpGeneralException {
- ArrayList<PGPSecretKey> keys = new ArrayList<PGPSecretKey>();
+ private ArrayList<UncachedSecretKey> getKeys(SectionView keysView) throws PgpGeneralException {
+ ArrayList<UncachedSecretKey> keys = new ArrayList<UncachedSecretKey>();
ViewGroup keyEditors = keysView.getEditors();
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 c954e6465..03483575c 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java
@@ -30,11 +30,11 @@ import android.widget.TextView;
import com.beardedhen.androidbootstrap.BootstrapButton;
-import org.spongycastle.openpgp.PGPSecretKey;
-import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
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.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.util.Log;
@@ -144,20 +144,17 @@ public class EncryptAsymmetricFragment extends Fragment {
*/
private void preselectKeys(long preselectedSignatureKeyId, long[] preselectedEncryptionKeyIds,
ProviderHelper providerHelper) {
+ // 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) {
- // TODO: don't use bouncy castle objects!
try {
- PGPSecretKeyRing keyRing = providerHelper.getPGPSecretKeyRingWithKeyId(
- preselectedSignatureKeyId);
-
- PGPSecretKey masterKey = keyRing.getSecretKey();
- if (masterKey != null) {
- PGPSecretKey signKey = PgpKeyHelper.getFirstSigningSubkey(keyRing);
- if (signKey != null) {
- setSignatureKeyId(masterKey.getKeyID());
- }
+ CachedPublicKeyRing keyring =
+ providerHelper.getCachedPublicKeyRing(
+ KeyRings.buildUnifiedKeyRingUri(preselectedSignatureKeyId));
+ if(keyring.hasAnySecret()) {
+ setSignatureKeyId(keyring.getMasterKeyId());
}
- } catch (ProviderHelper.NotFoundException e) {
+ } catch (PgpGeneralException e) {
Log.e(Constants.TAG, "key not found!", e);
}
}
@@ -165,14 +162,13 @@ public class EncryptAsymmetricFragment extends Fragment {
if (preselectedEncryptionKeyIds != null) {
Vector<Long> goodIds = new Vector<Long>();
for (int i = 0; i < preselectedEncryptionKeyIds.length; ++i) {
- // TODO One query per selected key?! wtf
try {
- long id = providerHelper.getMasterKeyId(
+ long id = providerHelper.getCachedPublicKeyRing(
KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(
- Long.toString(preselectedEncryptionKeyIds[i]))
- );
+ preselectedEncryptionKeyIds[i])
+ ).getMasterKeyId();
goodIds.add(id);
- } catch (ProviderHelper.NotFoundException e) {
+ } catch (PgpGeneralException e) {
Log.e(Constants.TAG, "key not found!", e);
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
index 35076287b..d33d450f9 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
@@ -42,6 +42,7 @@ import com.devspark.appmsg.AppMsg;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
@@ -415,8 +416,8 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O
// fill values for this action
Bundle data = new Bundle();
- // get selected key entries
- ArrayList<ImportKeysListEntry> selectedEntries = mListFragment.getSelectedData();
+ // get DATA from selected key entries
+ ArrayList<ParcelableKeyRing> selectedEntries = mListFragment.getSelectedData();
data.putParcelableArrayList(KeychainIntentService.IMPORT_KEY_LIST, selectedEntries);
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
@@ -442,7 +443,7 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O
data.putString(KeychainIntentService.DOWNLOAD_KEY_SERVER, mListFragment.getKeyServer());
// get selected key entries
- ArrayList<ImportKeysListEntry> selectedEntries = mListFragment.getSelectedData();
+ ArrayList<ImportKeysListEntry> selectedEntries = mListFragment.getSelectedEntries();
data.putParcelableArrayList(KeychainIntentService.DOWNLOAD_KEY_LIST, selectedEntries);
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
@@ -466,7 +467,7 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O
Bundle data = new Bundle();
// get selected key entries
- ArrayList<ImportKeysListEntry> selectedEntries = mListFragment.getSelectedData();
+ ArrayList<ImportKeysListEntry> selectedEntries = mListFragment.getSelectedEntries();
data.putParcelableArrayList(KeychainIntentService.DOWNLOAD_KEY_LIST, selectedEntries);
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java
index d9bd9b782..b70dd4d80 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java
@@ -23,6 +23,7 @@ import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
+import android.support.v4.util.LongSparseArray;
import android.view.View;
import android.widget.ListView;
@@ -31,6 +32,7 @@ import com.devspark.appmsg.AppMsg;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.Preferences;
+import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.ui.adapter.AsyncTaskResultWrapper;
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysAdapter;
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
@@ -67,6 +69,8 @@ public class ImportKeysListFragment extends ListFragment implements
private static final int LOADER_ID_SERVER_QUERY = 1;
private static final int LOADER_ID_KEYBASE = 2;
+ private LongSparseArray<ParcelableKeyRing> mCachedKeyData;
+
public byte[] getKeyBytes() {
return mKeyBytes;
}
@@ -91,8 +95,16 @@ public class ImportKeysListFragment extends ListFragment implements
return mAdapter.getData();
}
- public ArrayList<ImportKeysListEntry> getSelectedData() {
- return mAdapter.getSelectedData();
+ public ArrayList<ParcelableKeyRing> getSelectedData() {
+ ArrayList<ParcelableKeyRing> result = new ArrayList<ParcelableKeyRing>();
+ for(ImportKeysListEntry entry : getSelectedEntries()) {
+ result.add(mCachedKeyData.get(entry.getKeyId()));
+ }
+ return result;
+ }
+
+ public ArrayList<ImportKeysListEntry> getSelectedEntries() {
+ return mAdapter.getSelectedEntries();
}
/**
@@ -120,8 +132,7 @@ public class ImportKeysListFragment extends ListFragment implements
mActivity = getActivity();
- // Give some text to display if there is no data. In a real
- // application this would come from a resource.
+ // Give some text to display if there is no data.
setEmptyText(mActivity.getString(R.string.error_nothing_import));
// Create an empty adapter we will use to display the loaded data.
@@ -252,11 +263,15 @@ public class ImportKeysListFragment extends ListFragment implements
Exception error = data.getError();
+ // free old cached key data
+ mCachedKeyData = null;
+
switch (loader.getId()) {
case LOADER_ID_BYTES:
if (error == null) {
// No error
+ mCachedKeyData = ((ImportKeysListLoader) loader).getParcelableRings();
} else if (error instanceof ImportKeysListLoader.FileHasNoContent) {
AppMsg.makeText(getActivity(), R.string.error_import_file_no_content,
AppMsg.STYLE_ALERT).show();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
index e38ed6e67..9c90b5eb7 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
@@ -317,7 +317,7 @@ public class KeyListFragment extends LoaderFragment
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
Intent viewIntent = new Intent(getActivity(), ViewKeyActivity.class);
viewIntent.setData(
- KeyRings.buildGenericKeyRingUri(Long.toString(mAdapter.getMasterKeyId(position))));
+ KeyRings.buildGenericKeyRingUri(mAdapter.getMasterKeyId(position)));
startActivity(viewIntent);
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java
index 38a0c8478..9ddc8e3e1 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java
@@ -84,7 +84,7 @@ public class SelectSecretKeyFragment extends ListFragment implements
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
long masterKeyId = mAdapter.getMasterKeyId(position);
- Uri result = KeyRings.buildGenericKeyRingUri(String.valueOf(masterKeyId));
+ Uri result = KeyRings.buildGenericKeyRingUri(masterKeyId);
// return data to activity, which results in finishing it
mActivity.afterListSelection(result);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyLayoutFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyLayoutFragment.java
index 8db750917..fe2ecf3a3 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyLayoutFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyLayoutFragment.java
@@ -132,7 +132,7 @@ public class SelectSecretKeyLayoutFragment extends Fragment implements LoaderMan
//For AppSettingsFragment
public void selectKey(long masterKeyId) {
- Uri buildUri = KeychainContract.KeyRings.buildGenericKeyRingUri(String.valueOf(masterKeyId));
+ Uri buildUri = KeychainContract.KeyRings.buildGenericKeyRingUri(masterKeyId);
mReceivedUri = buildUri;
getActivity().getSupportLoaderManager().restartLoader(LOADER_ID, null, this);
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java
index f78c30820..ae0206ab1 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java
@@ -32,24 +32,17 @@ import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
-import org.spongycastle.bcpg.SignatureSubpacket;
-import org.spongycastle.bcpg.SignatureSubpacketTags;
-import org.spongycastle.bcpg.sig.RevocationReason;
-import org.spongycastle.openpgp.PGPException;
-import org.spongycastle.openpgp.PGPKeyRing;
-import org.spongycastle.openpgp.PGPSignature;
-import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
+import org.sufficientlysecure.keychain.pgp.WrappedPublicKeyRing;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
-import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.pgp.WrappedSignature;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.util.Log;
-import java.security.SignatureException;
import java.util.Date;
public class ViewCertActivity extends ActionBarActivity
@@ -146,32 +139,25 @@ public class ViewCertActivity extends ActionBarActivity
mCertifierUid.setText(R.string.unknown_uid);
}
- PGPSignature sig = PgpConversionHelper.BytesToPGPSignature(data.getBlob(INDEX_DATA));
+ WrappedSignature sig = WrappedSignature.fromBytes(data.getBlob(INDEX_DATA));
try {
ProviderHelper providerHelper = new ProviderHelper(this);
- PGPKeyRing signeeRing = providerHelper.getPGPKeyRing(
- KeychainContract.KeyRingData.buildPublicKeyRingUri(
- Long.toString(data.getLong(INDEX_MASTER_KEY_ID)))
- );
- PGPKeyRing signerRing = providerHelper.getPGPKeyRing(
- KeychainContract.KeyRingData.buildPublicKeyRingUri(
- Long.toString(sig.getKeyID()))
- );
+
+ WrappedPublicKeyRing signeeRing =
+ providerHelper.getWrappedPublicKeyRing(data.getLong(INDEX_MASTER_KEY_ID));
+ WrappedPublicKeyRing signerRing =
+ providerHelper.getWrappedPublicKeyRing(sig.getKeyId());
try {
- sig.init(new JcaPGPContentVerifierBuilderProvider().setProvider(
- Constants.BOUNCY_CASTLE_PROVIDER_NAME), signerRing.getPublicKey());
- if (sig.verifyCertification(signeeUid, signeeRing.getPublicKey())) {
+ sig.init(signerRing.getSubkey());
+ if (sig.verifySignature(signeeRing.getSubkey(), signeeUid)) {
mStatus.setText(R.string.cert_verify_ok);
mStatus.setTextColor(getResources().getColor(R.color.bbutton_success));
} else {
mStatus.setText(R.string.cert_verify_failed);
mStatus.setTextColor(getResources().getColor(R.color.alert));
}
- } catch (SignatureException e) {
- mStatus.setText(R.string.cert_verify_error);
- mStatus.setTextColor(getResources().getColor(R.color.alert));
- } catch (PGPException e) {
+ } catch (PgpGeneralException e) {
mStatus.setText(R.string.cert_verify_error);
mStatus.setTextColor(getResources().getColor(R.color.alert));
}
@@ -185,29 +171,26 @@ public class ViewCertActivity extends ActionBarActivity
mRowReason.setVisibility(View.GONE);
switch (data.getInt(INDEX_TYPE)) {
- case PGPSignature.DEFAULT_CERTIFICATION:
+ case WrappedSignature.DEFAULT_CERTIFICATION:
mType.setText(R.string.cert_default);
break;
- case PGPSignature.NO_CERTIFICATION:
+ case WrappedSignature.NO_CERTIFICATION:
mType.setText(R.string.cert_none);
break;
- case PGPSignature.CASUAL_CERTIFICATION:
+ case WrappedSignature.CASUAL_CERTIFICATION:
mType.setText(R.string.cert_casual);
break;
- case PGPSignature.POSITIVE_CERTIFICATION:
+ case WrappedSignature.POSITIVE_CERTIFICATION:
mType.setText(R.string.cert_positive);
break;
- case PGPSignature.CERTIFICATION_REVOCATION: {
+ case WrappedSignature.CERTIFICATION_REVOCATION: {
mType.setText(R.string.cert_revoke);
- if (sig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.REVOCATION_REASON)) {
- SignatureSubpacket p = sig.getHashedSubPackets().getSubpacket(
- SignatureSubpacketTags.REVOCATION_REASON);
- // For some reason, this is missing in SignatureSubpacketInputStream:146
- if (!(p instanceof RevocationReason)) {
- p = new RevocationReason(false, p.getData());
+ if (sig.isRevocation()) {
+ try {
+ mReason.setText(sig.getRevocationReason());
+ } catch(PgpGeneralException e) {
+ mReason.setText(R.string.none);
}
- String reason = ((RevocationReason) p).getRevocationDescription();
- mReason.setText(reason);
mRowReason.setVisibility(View.VISIBLE);
}
break;
@@ -223,14 +206,11 @@ public class ViewCertActivity extends ActionBarActivity
try {
ProviderHelper providerHelper = new ProviderHelper(ViewCertActivity.this);
- long signerMasterKeyId = providerHelper.getMasterKeyId(
- KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(Long.toString(mCertifierKeyId))
- );
- viewIntent.setData(KeyRings.buildGenericKeyRingUri(
- Long.toString(signerMasterKeyId))
- );
+ long signerMasterKeyId = providerHelper.getCachedPublicKeyRing(
+ KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(mCertifierKeyId)).getMasterKeyId();
+ viewIntent.setData(KeyRings.buildGenericKeyRingUri(signerMasterKeyId));
startActivity(viewIntent);
- } catch (ProviderHelper.NotFoundException e) {
+ } catch (PgpGeneralException e) {
// TODO notify user of this, maybe offer download?
Log.e(Constants.TAG, "key not found!", e);
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
index 8c52e6f22..bed116f5f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
@@ -326,10 +326,10 @@ public class ViewKeyActivity extends ActionBarActivity implements
try {
Uri blobUri =
KeychainContract.KeyRingData.buildPublicKeyRingUri(dataUri);
- mNfcKeyringBytes = mProviderHelper.getPGPKeyRing(
- blobUri).getEncoded();
- } catch (IOException e) {
- Log.e(Constants.TAG, "Error parsing keyring", e);
+ mNfcKeyringBytes = (byte[]) mProviderHelper.getGenericData(
+ blobUri,
+ KeychainContract.KeyRingData.KEY_RING_DATA,
+ ProviderHelper.FIELD_TYPE_BLOB);
} catch (ProviderHelper.NotFoundException e) {
Log.e(Constants.TAG, "key not found!", e);
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java
index d5658586d..3cd43638a 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java
@@ -33,10 +33,10 @@ import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.TextView;
-import org.spongycastle.openpgp.PGPSignature;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
+import org.sufficientlysecure.keychain.pgp.WrappedSignature;
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
import org.sufficientlysecure.keychain.util.Log;
@@ -227,19 +227,19 @@ public class ViewKeyCertsFragment extends LoaderFragment
wSignerKeyId.setText(signerKeyId);
switch (cursor.getInt(mIndexType)) {
- case PGPSignature.DEFAULT_CERTIFICATION: // 0x10
+ case WrappedSignature.DEFAULT_CERTIFICATION: // 0x10
wSignStatus.setText(R.string.cert_default);
break;
- case PGPSignature.NO_CERTIFICATION: // 0x11
+ case WrappedSignature.NO_CERTIFICATION: // 0x11
wSignStatus.setText(R.string.cert_none);
break;
- case PGPSignature.CASUAL_CERTIFICATION: // 0x12
+ case WrappedSignature.CASUAL_CERTIFICATION: // 0x12
wSignStatus.setText(R.string.cert_casual);
break;
- case PGPSignature.POSITIVE_CERTIFICATION: // 0x13
+ case WrappedSignature.POSITIVE_CERTIFICATION: // 0x13
wSignStatus.setText(R.string.cert_positive);
break;
- case PGPSignature.CERTIFICATION_REVOCATION: // 0x30
+ case WrappedSignature.CERTIFICATION_REVOCATION: // 0x30
wSignStatus.setText(R.string.cert_revoke);
break;
}
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 026417776..c16bb82af 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java
@@ -32,7 +32,9 @@ import android.widget.ListView;
import com.devspark.appmsg.AppMsg;
import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.R;import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
@@ -235,14 +237,16 @@ public class ViewKeyMainFragment extends LoaderFragment implements
return;
}
try {
- long keyId = new ProviderHelper(getActivity()).extractOrGetMasterKeyId(dataUri);
+ long keyId = new ProviderHelper(getActivity())
+ .getCachedPublicKeyRing(dataUri)
+ .extractOrGetMasterKeyId();
long[] encryptionKeyIds = new long[]{keyId};
Intent intent = new Intent(getActivity(), EncryptActivity.class);
intent.setAction(EncryptActivity.ACTION_ENCRYPT);
intent.putExtra(EncryptActivity.EXTRA_ENCRYPTION_KEY_IDS, encryptionKeyIds);
// used instead of startActivity set actionbar based on callingPackage
startActivityForResult(intent, 0);
- } catch (ProviderHelper.NotFoundException e) {
+ } catch (PgpGeneralException e) {
Log.e(Constants.TAG, "key not found!", e);
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyShareFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyShareFragment.java
index b3655133d..a264a804f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyShareFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyShareFragment.java
@@ -41,6 +41,7 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
@@ -190,6 +191,9 @@ public class ViewKeyShareFragment extends LoaderFragment implements
}
startActivity(Intent.createChooser(sendIntent, title));
}
+ } catch (PgpGeneralException e) {
+ Log.e(Constants.TAG, "error processing key!", e);
+ AppMsg.makeText(getActivity(), R.string.error_key_processing, AppMsg.STYLE_ALERT).show();
} catch (IOException e) {
Log.e(Constants.TAG, "error processing key!", e);
AppMsg.makeText(getActivity(), R.string.error_key_processing, AppMsg.STYLE_ALERT).show();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java
index 9573efdfe..114c6afae 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java
@@ -32,6 +32,7 @@ import android.widget.TextView;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
+import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.util.Highlighter;
@@ -83,7 +84,7 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> {
return mData;
}
- public ArrayList<ImportKeysListEntry> getSelectedData() {
+ public ArrayList<ImportKeysListEntry> getSelectedEntries() {
ArrayList<ImportKeysListEntry> selectedData = new ArrayList<ImportKeysListEntry>();
for (ImportKeysListEntry entry : mData) {
if (entry.isSelected()) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java
index b6c829677..03a82696d 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java
@@ -19,19 +19,19 @@ package org.sufficientlysecure.keychain.ui.adapter;
import android.content.Context;
import android.support.v4.content.AsyncTaskLoader;
+import android.support.v4.util.LongSparseArray;
-import org.spongycastle.openpgp.PGPKeyRing;
-import org.spongycastle.openpgp.PGPObjectFactory;
-import org.spongycastle.openpgp.PGPUtil;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
+import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
+import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.PositionAwareInputStream;
import java.io.BufferedInputStream;
-import java.io.InputStream;
import java.util.ArrayList;
+import java.util.List;
public class ImportKeysListLoader
extends AsyncTaskLoader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> {
@@ -56,6 +56,7 @@ public class ImportKeysListLoader
final InputData mInputData;
ArrayList<ImportKeysListEntry> mData = new ArrayList<ImportKeysListEntry>();
+ LongSparseArray<ParcelableKeyRing> mParcelableRings = new LongSparseArray<ParcelableKeyRing>();
AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> mEntryListWrapper;
public ImportKeysListLoader(Context context, InputData inputData) {
@@ -107,6 +108,10 @@ public class ImportKeysListLoader
super.deliverResult(data);
}
+ public LongSparseArray<ParcelableKeyRing> getParcelableRings() {
+ return mParcelableRings;
+ }
+
/**
* Reads all PGPKeyRing objects from input
*
@@ -116,7 +121,6 @@ public class ImportKeysListLoader
private void generateListOfKeyrings(InputData inputData) {
boolean isEmpty = true;
- int nonPgpCounter = 0;
PositionAwareInputStream progressIn = new PositionAwareInputStream(
inputData.getInputStream());
@@ -129,28 +133,18 @@ public class ImportKeysListLoader
// read all available blocks... (asc files can contain many blocks with BEGIN END)
while (bufferedInput.available() > 0) {
- isEmpty = false;
- InputStream in = PGPUtil.getDecoderStream(bufferedInput);
- PGPObjectFactory objectFactory = new PGPObjectFactory(in);
-
- // go through all objects in this block
- Object obj;
- while ((obj = objectFactory.nextObject()) != null) {
- Log.d(Constants.TAG, "Found class: " + obj.getClass());
-
- if (obj instanceof PGPKeyRing) {
- PGPKeyRing newKeyring = (PGPKeyRing) obj;
- addToData(newKeyring);
- } else {
- Log.e(Constants.TAG, "Object not recognized as PGPKeyRing!");
- nonPgpCounter++;
- }
+ // todo deal with non-keyring objects?
+ List<UncachedKeyRing> rings = UncachedKeyRing.fromStream(bufferedInput);
+ for(UncachedKeyRing key : rings) {
+ ImportKeysListEntry item = new ImportKeysListEntry(getContext(), key);
+ mData.add(item);
+ mParcelableRings.put(key.getMasterKeyId(), new ParcelableKeyRing(key.getEncoded()));
+ isEmpty = false;
}
}
} catch (Exception e) {
Log.e(Constants.TAG, "Exception on parsing key file!", e);
mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mData, e);
- nonPgpCounter = 0;
}
if (isEmpty) {
@@ -158,16 +152,6 @@ public class ImportKeysListLoader
mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>
(mData, new FileHasNoContent());
}
-
- if (nonPgpCounter > 0) {
- mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>
- (mData, new NonPgpPart(nonPgpCounter));
- }
- }
-
- private void addToData(PGPKeyRing keyring) {
- ImportKeysListEntry item = new ImportKeysListEntry(getContext(), keyring);
- mData.add(item);
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java
index 12fd77141..5f47ee13c 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java
@@ -41,17 +41,12 @@ import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
import android.widget.Toast;
-import org.spongycastle.openpgp.PGPException;
-import org.spongycastle.openpgp.PGPPrivateKey;
-import org.spongycastle.openpgp.PGPSecretKey;
-import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
-import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
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.WrappedSecretKey;
+import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
-import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.util.Log;
@@ -106,8 +101,12 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
long secretKeyId) throws PgpGeneralException {
// check if secret key has a passphrase
if (!(secretKeyId == Constants.key.symmetric || secretKeyId == Constants.key.none)) {
- if (!PassphraseCacheService.hasPassphrase(context, secretKeyId)) {
- throw new PgpGeneralException("No passphrase! No passphrase dialog needed!");
+ try {
+ if (!new ProviderHelper(context).getWrappedSecretKeyRing(secretKeyId).hasPassphrase()) {
+ throw new PgpGeneralException("No passphrase! No passphrase dialog needed!");
+ }
+ } catch(ProviderHelper.NotFoundException e) {
+ throw new PgpGeneralException("Error: Key not found!", e);
}
}
@@ -139,18 +138,24 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
alert.setTitle(R.string.title_authentication);
- final PGPSecretKey secretKey;
- final String userId;
+ final WrappedSecretKeyRing secretRing;
+ String userId;
if (secretKeyId == Constants.key.symmetric || secretKeyId == Constants.key.none) {
- secretKey = null;
alert.setMessage(R.string.passphrase_for_symmetric_encryption);
+ secretRing = null;
} else {
try {
ProviderHelper helper = new ProviderHelper(activity);
- secretKey = helper.getPGPSecretKeyRing(secretKeyId).getSecretKey();
- userId = (String) helper.getUnifiedData(secretKeyId,
- KeychainContract.KeyRings.USER_ID, ProviderHelper.FIELD_TYPE_STRING);
+ secretRing = helper.getWrappedSecretKeyRing(secretKeyId);
+ // yes the inner try/catch block is necessary, otherwise the final variable
+ // above can't be statically verified to have been set in all cases because
+ // the catch clause doesn't return.
+ try {
+ userId = secretRing.getPrimaryUserId();
+ } catch (PgpGeneralException e) {
+ userId = null;
+ }
} catch (ProviderHelper.NotFoundException e) {
alert.setTitle(R.string.title_key_not_found);
alert.setMessage(getString(R.string.key_not_found, secretKeyId));
@@ -179,76 +184,59 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
@Override
public void onClick(DialogInterface dialog, int id) {
dismiss();
- long curKeyIndex = 1;
- boolean keyOK = true;
+
String passphrase = mPassphraseEditText.getText().toString();
- long keyId;
- PGPSecretKey clickSecretKey = secretKey;
-
- if (clickSecretKey != null) {
- while (keyOK) {
- if (clickSecretKey != null) { // check again for loop
- try {
- PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
- passphrase.toCharArray());
- PGPPrivateKey testKey = clickSecretKey
- .extractPrivateKey(keyDecryptor);
- if (testKey == null) {
- if (!clickSecretKey.isMasterKey()) {
- Toast.makeText(activity,
- R.string.error_could_not_extract_private_key,
- Toast.LENGTH_SHORT).show();
-
- sendMessageToHandler(MESSAGE_CANCEL);
- return;
- } else {
- try {
- clickSecretKey = PgpKeyHelper.getKeyNum(new ProviderHelper(activity)
- .getPGPSecretKeyRingWithKeyId(secretKeyId),
- curKeyIndex
- );
- } catch (ProviderHelper.NotFoundException e) {
- Log.e(Constants.TAG, "key not found!", e);
- }
- curKeyIndex++; // does post-increment work like C?
- continue;
- }
- } else {
- keyOK = false;
- }
- } catch (PGPException e) {
- Toast.makeText(activity, R.string.wrong_passphrase,
- Toast.LENGTH_SHORT).show();
-
- sendMessageToHandler(MESSAGE_CANCEL);
- return;
- }
- } else {
- Toast.makeText(activity, R.string.error_could_not_extract_private_key,
- Toast.LENGTH_SHORT).show();
-
- sendMessageToHandler(MESSAGE_CANCEL);
- return; // ran out of keys to try
+
+ // Early breakout if we are dealing with a symmetric key
+ if (secretRing == null) {
+ PassphraseCacheService.addCachedPassphrase(activity, Constants.key.symmetric, passphrase);
+ // also return passphrase back to activity
+ Bundle data = new Bundle();
+ data.putString(MESSAGE_DATA_PASSPHRASE, passphrase);
+ sendMessageToHandler(MESSAGE_OKAY, data);
+ return;
+ }
+
+ WrappedSecretKey unlockedSecretKey = null;
+
+ for(WrappedSecretKey clickSecretKey : secretRing.iterator()) {
+ try {
+ boolean unlocked = clickSecretKey.unlock(passphrase);
+ if (unlocked) {
+ unlockedSecretKey = clickSecretKey;
+ break;
}
+ } catch (PgpGeneralException e) {
+ Toast.makeText(activity, R.string.error_could_not_extract_private_key,
+ Toast.LENGTH_SHORT).show();
+
+ sendMessageToHandler(MESSAGE_CANCEL);
+ return; // ran out of keys to try
}
- keyId = secretKey.getKeyID();
- } else {
- keyId = Constants.key.symmetric;
}
+ // Means we got an exception every time
+ if (unlockedSecretKey == null) {
+ Toast.makeText(activity, R.string.wrong_passphrase,
+ Toast.LENGTH_SHORT).show();
+
+ sendMessageToHandler(MESSAGE_CANCEL);
+ return;
+ }
+
+ long masterKeyId = secretRing.getMasterKeyId();
+
// cache the new passphrase
Log.d(Constants.TAG, "Everything okay! Caching entered passphrase");
- PassphraseCacheService.addCachedPassphrase(activity, keyId, passphrase);
- if (!keyOK && clickSecretKey.getKeyID() != keyId) {
- PassphraseCacheService.addCachedPassphrase(activity, clickSecretKey.getKeyID(),
- passphrase);
+ PassphraseCacheService.addCachedPassphrase(activity, masterKeyId, passphrase);
+ if (unlockedSecretKey.getKeyId() != masterKeyId) {
+ PassphraseCacheService.addCachedPassphrase(
+ activity, unlockedSecretKey.getKeyId(), passphrase);
}
// also return passphrase back to activity
Bundle data = new Bundle();
data.putString(MESSAGE_DATA_PASSPHRASE, passphrase);
-
sendMessageToHandler(MESSAGE_OKAY, data);
}
});
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java
index 1628c9e95..40fe7665c 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java
@@ -38,22 +38,18 @@ import android.widget.TextView;
import com.beardedhen.androidbootstrap.BootstrapButton;
-import org.spongycastle.bcpg.sig.KeyFlags;
-import org.spongycastle.openpgp.PGPPublicKey;
-import org.spongycastle.openpgp.PGPSecretKey;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
-import org.sufficientlysecure.keychain.util.Choice;
+import org.sufficientlysecure.keychain.pgp.UncachedSecretKey;
import java.text.DateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.TimeZone;
-import java.util.Vector;
public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
- private PGPSecretKey mKey;
+ private UncachedSecretKey mKey;
private EditorListener mEditorListener = null;
@@ -208,7 +204,7 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
}
}
- public void setValue(PGPSecretKey key, boolean isMasterKey, int usage, boolean isNewKey) {
+ public void setValue(UncachedSecretKey key, boolean isMasterKey, int usage, boolean isNewKey) {
mKey = key;
mIsMasterKey = isMasterKey;
@@ -216,13 +212,12 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
mDeleteButton.setVisibility(View.INVISIBLE);
}
- mAlgorithm.setText(PgpKeyHelper.getAlgorithmInfo(getContext(), key));
- String keyIdStr = PgpKeyHelper.convertKeyIdToHex(key.getKeyID());
+ mAlgorithm.setText(PgpKeyHelper.getAlgorithmInfo(getContext(), key.getAlgorithm()));
+ String keyIdStr = PgpKeyHelper.convertKeyIdToHex(key.getKeyId());
mKeyId.setText(keyIdStr);
- Vector<Choice> choices = new Vector<Choice>();
- boolean isElGamalKey = (key.getPublicKey().getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT);
- boolean isDSAKey = (key.getPublicKey().getAlgorithm() == PGPPublicKey.DSA);
+ boolean isElGamalKey = (key.isElGamalEncrypt());
+ boolean isDSAKey = (key.isDSA());
if (isElGamalKey) {
mChkSign.setVisibility(View.INVISIBLE);
TableLayout table = (TableLayout) findViewById(R.id.table_keylayout);
@@ -248,38 +243,45 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
mIsNewKey = isNewKey;
if (isNewKey) {
mUsage = usage;
- mChkCertify.setChecked((usage & KeyFlags.CERTIFY_OTHER) == KeyFlags.CERTIFY_OTHER);
- mChkSign.setChecked((usage & KeyFlags.SIGN_DATA) == KeyFlags.SIGN_DATA);
- mChkEncrypt.setChecked(((usage & KeyFlags.ENCRYPT_COMMS) == KeyFlags.ENCRYPT_COMMS) ||
- ((usage & KeyFlags.ENCRYPT_STORAGE) == KeyFlags.ENCRYPT_STORAGE));
- mChkAuthenticate.setChecked((usage & KeyFlags.AUTHENTICATION) == KeyFlags.AUTHENTICATION);
+ mChkCertify.setChecked(
+ (usage & UncachedSecretKey.CERTIFY_OTHER) == UncachedSecretKey.CERTIFY_OTHER);
+ mChkSign.setChecked(
+ (usage & UncachedSecretKey.SIGN_DATA) == UncachedSecretKey.SIGN_DATA);
+ mChkEncrypt.setChecked(
+ ((usage & UncachedSecretKey.ENCRYPT_COMMS) == UncachedSecretKey.ENCRYPT_COMMS) ||
+ ((usage & UncachedSecretKey.ENCRYPT_STORAGE) == UncachedSecretKey.ENCRYPT_STORAGE));
+ mChkAuthenticate.setChecked(
+ (usage & UncachedSecretKey.AUTHENTICATION) == UncachedSecretKey.AUTHENTICATION);
} else {
- mUsage = PgpKeyHelper.getKeyUsage(key);
+ mUsage = key.getKeyUsage();
mOriginalUsage = mUsage;
if (key.isMasterKey()) {
- mChkCertify.setChecked(PgpKeyHelper.isCertificationKey(key));
+ mChkCertify.setChecked(key.canCertify());
}
- mChkSign.setChecked(PgpKeyHelper.isSigningKey(key));
- mChkEncrypt.setChecked(PgpKeyHelper.isEncryptionKey(key));
- mChkAuthenticate.setChecked(PgpKeyHelper.isAuthenticationKey(key));
+ mChkSign.setChecked(key.canSign());
+ mChkEncrypt.setChecked(key.canEncrypt());
+ mChkAuthenticate.setChecked(key.canAuthenticate());
}
- Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
- cal.setTime(PgpKeyHelper.getCreationDate(key));
- setCreatedDate(cal);
- cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
- Date expiryDate = PgpKeyHelper.getExpiryDate(key);
+ {
+ Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
+ cal.setTime(key.getCreationTime());
+ setCreatedDate(cal);
+ }
+
+ Date expiryDate = key.getExpiryTime();
if (expiryDate == null) {
setExpiryDate(null);
} else {
- cal.setTime(PgpKeyHelper.getExpiryDate(key));
+ Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
+ cal.setTime(expiryDate);
setExpiryDate(cal);
mOriginalExpiryDate = cal;
}
}
- public PGPSecretKey getValue() {
+ public UncachedSecretKey getValue() {
return mKey;
}
@@ -320,16 +322,16 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
}
public int getUsage() {
- mUsage = (mUsage & ~KeyFlags.CERTIFY_OTHER) |
- (mChkCertify.isChecked() ? KeyFlags.CERTIFY_OTHER : 0);
- mUsage = (mUsage & ~KeyFlags.SIGN_DATA) |
- (mChkSign.isChecked() ? KeyFlags.SIGN_DATA : 0);
- mUsage = (mUsage & ~KeyFlags.ENCRYPT_COMMS) |
- (mChkEncrypt.isChecked() ? KeyFlags.ENCRYPT_COMMS : 0);
- mUsage = (mUsage & ~KeyFlags.ENCRYPT_STORAGE) |
- (mChkEncrypt.isChecked() ? KeyFlags.ENCRYPT_STORAGE : 0);
- mUsage = (mUsage & ~KeyFlags.AUTHENTICATION) |
- (mChkAuthenticate.isChecked() ? KeyFlags.AUTHENTICATION : 0);
+ mUsage = (mUsage & ~UncachedSecretKey.CERTIFY_OTHER) |
+ (mChkCertify.isChecked() ? UncachedSecretKey.CERTIFY_OTHER : 0);
+ mUsage = (mUsage & ~UncachedSecretKey.SIGN_DATA) |
+ (mChkSign.isChecked() ? UncachedSecretKey.SIGN_DATA : 0);
+ mUsage = (mUsage & ~UncachedSecretKey.ENCRYPT_COMMS) |
+ (mChkEncrypt.isChecked() ? UncachedSecretKey.ENCRYPT_COMMS : 0);
+ mUsage = (mUsage & ~UncachedSecretKey.ENCRYPT_STORAGE) |
+ (mChkEncrypt.isChecked() ? UncachedSecretKey.ENCRYPT_STORAGE : 0);
+ mUsage = (mUsage & ~UncachedSecretKey.AUTHENTICATION) |
+ (mChkAuthenticate.isChecked() ? UncachedSecretKey.AUTHENTICATION : 0);
return mUsage;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java
index a7719012a..3e8e18ce5 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java
@@ -35,10 +35,9 @@ import android.widget.TextView;
import com.beardedhen.androidbootstrap.BootstrapButton;
-import org.spongycastle.openpgp.PGPKeyFlags;
-import org.spongycastle.openpgp.PGPSecretKey;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
+import org.sufficientlysecure.keychain.pgp.UncachedSecretKey;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
@@ -63,7 +62,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
private int mNewKeySize;
private boolean mOldItemDeleted = false;
private ArrayList<String> mDeletedIDs = new ArrayList<String>();
- private ArrayList<PGPSecretKey> mDeletedKeys = new ArrayList<PGPSecretKey>();
+ private ArrayList<UncachedSecretKey> mDeletedKeys = new ArrayList<UncachedSecretKey>();
private boolean mCanBeEdited = true;
private ActionBarActivity mActivity;
@@ -227,7 +226,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
return mDeletedIDs;
}
- public ArrayList<PGPSecretKey> getDeletedKeys() {
+ public ArrayList<UncachedSecretKey> getDeletedKeys() {
return mDeletedKeys;
}
@@ -325,7 +324,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
this.updateEditorsVisible();
}
- public void setKeys(Vector<PGPSecretKey> list, Vector<Integer> usages, boolean newKeys) {
+ public void setKeys(Vector<UncachedSecretKey> list, Vector<Integer> usages, boolean newKeys) {
if (mType != TYPE_KEY) {
return;
}
@@ -358,9 +357,9 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
String passphrase;
if (mEditors.getChildCount() > 0) {
- PGPSecretKey masterKey = ((KeyEditor) mEditors.getChildAt(0)).getValue();
+ UncachedSecretKey masterKey = ((KeyEditor) mEditors.getChildAt(0)).getValue();
passphrase = PassphraseCacheService
- .getCachedPassphrase(mActivity, masterKey.getKeyID());
+ .getCachedPassphrase(mActivity, masterKey.getKeyId());
isMasterKey = false;
} else {
passphrase = "";
@@ -395,7 +394,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
// get new key from data bundle returned from service
Bundle data = message.getData();
- PGPSecretKey newKey = (PGPSecretKey) PgpConversionHelper
+ UncachedSecretKey newKey = PgpConversionHelper
.BytesToPGPSecretKey(data
.getByteArray(KeychainIntentService.RESULT_NEW_KEY));
addGeneratedKeyToView(newKey);
@@ -413,14 +412,14 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
mActivity.startService(intent);
}
- private void addGeneratedKeyToView(PGPSecretKey newKey) {
+ private void addGeneratedKeyToView(UncachedSecretKey newKey) {
// add view with new key
KeyEditor view = (KeyEditor) mInflater.inflate(R.layout.edit_key_key_item,
mEditors, false);
view.setEditorListener(SectionView.this);
int usage = 0;
if (mEditors.getChildCount() == 0) {
- usage = PGPKeyFlags.CAN_CERTIFY;
+ usage = UncachedSecretKey.CERTIFY_OTHER;
}
view.setValue(newKey, newKey.isMasterKey(), usage, true);
mEditors.addView(view);
diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml
index 1ba8a6d2d..eeb6b3742 100644
--- a/OpenKeychain/src/main/res/values/strings.xml
+++ b/OpenKeychain/src/main/res/values/strings.xml
@@ -517,6 +517,7 @@
<string name="title_view_cert">View Certificate Details</string>
<string name="unknown_algorithm">unknown</string>
<string name="can_sign_not">cannot sign</string>
+ <string name="error_encoding">Encoding error</string>
<string name="error_no_encrypt_subkey">No encryption subkey available!</string>
</resources>
diff --git a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/PgpKeyOperationTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/PgpKeyOperationTest.java
deleted file mode 100644
index 72f29a1e3..000000000
--- a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/PgpKeyOperationTest.java
+++ /dev/null
@@ -1,46 +0,0 @@
-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/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/RobolectricGradleTestRunner.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/RobolectricGradleTestRunner.java
deleted file mode 100644
index b64ffde07..000000000
--- a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/RobolectricGradleTestRunner.java
+++ /dev/null
@@ -1,23 +0,0 @@
-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));
- }
-}
-