aboutsummaryrefslogtreecommitdiffstats
path: root/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp
diff options
context:
space:
mode:
Diffstat (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp')
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java (renamed from OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedKeyRing.java)36
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKey.java (renamed from OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedPublicKey.java)4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKeyRing.java (renamed from OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedPublicKeyRing.java)55
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java (renamed from OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKey.java)22
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKeyRing.java (renamed from OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKeyRing.java)55
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java8
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java28
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java7
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java30
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java409
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java10
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java246
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java96
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java41
14 files changed, 686 insertions, 361 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java
index 6f3068261..ee0dfefa4 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java
@@ -16,13 +16,11 @@ import java.io.OutputStream;
* getter method.
*
*/
-public abstract class WrappedKeyRing extends KeyRing {
+public abstract class CanonicalizedKeyRing extends KeyRing {
- private final boolean mHasAnySecret;
private final int mVerified;
- WrappedKeyRing(boolean hasAnySecret, int verified) {
- mHasAnySecret = hasAnySecret;
+ CanonicalizedKeyRing(int verified) {
mVerified = verified;
}
@@ -30,17 +28,17 @@ public abstract class WrappedKeyRing extends KeyRing {
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();
- };
+ return getPublicKey().getPrimaryUserId();
+ }
+
+ public String getPrimaryUserIdWithFallback() throws PgpGeneralException {
+ return getPublicKey().getPrimaryUserIdWithFallback();
+ }
public boolean isRevoked() throws PgpGeneralException {
// Is the master key revoked?
@@ -52,7 +50,7 @@ public abstract class WrappedKeyRing extends KeyRing {
}
public long getEncryptId() throws PgpGeneralException {
- for(WrappedPublicKey key : publicKeyIterator()) {
+ for(CanonicalizedPublicKey key : publicKeyIterator()) {
if(key.canEncrypt()) {
return key.getKeyId();
}
@@ -70,7 +68,7 @@ public abstract class WrappedKeyRing extends KeyRing {
}
public long getSignId() throws PgpGeneralException {
- for(WrappedPublicKey key : publicKeyIterator()) {
+ for(CanonicalizedPublicKey key : publicKeyIterator()) {
if(key.canSign()) {
return key.getKeyId();
}
@@ -99,10 +97,18 @@ public abstract class WrappedKeyRing extends KeyRing {
abstract PGPKeyRing getRing();
- abstract public IterableIterator<WrappedPublicKey> publicKeyIterator();
+ abstract public IterableIterator<CanonicalizedPublicKey> publicKeyIterator();
- public UncachedKeyRing getUncached() {
- return new UncachedKeyRing(getRing());
+ public CanonicalizedPublicKey getPublicKey() {
+ return new CanonicalizedPublicKey(this, getRing().getPublicKey());
+ }
+
+ public CanonicalizedPublicKey getPublicKey(long id) {
+ return new CanonicalizedPublicKey(this, getRing().getPublicKey(id));
+ }
+
+ public byte[] getEncoded() throws IOException {
+ return getRing().getEncoded();
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedPublicKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKey.java
index 69a4fbdee..981caad49 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedPublicKey.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKey.java
@@ -14,12 +14,12 @@ import org.sufficientlysecure.keychain.util.IterableIterator;
* stored in the database.
*
*/
-public class WrappedPublicKey extends UncachedPublicKey {
+public class CanonicalizedPublicKey extends UncachedPublicKey {
// this is the parent key ring
final KeyRing mRing;
- WrappedPublicKey(KeyRing ring, PGPPublicKey key) {
+ CanonicalizedPublicKey(KeyRing ring, PGPPublicKey key) {
super(key);
mRing = ring;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedPublicKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKeyRing.java
index b2abf15a4..70288dceb 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedPublicKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKeyRing.java
@@ -1,42 +1,45 @@
package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.bcpg.ArmoredOutputStream;
-import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPObjectFactory;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPPublicKeyRing;
-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.util.Iterator;
-public class WrappedPublicKeyRing extends WrappedKeyRing {
+public class CanonicalizedPublicKeyRing extends CanonicalizedKeyRing {
private PGPPublicKeyRing mRing;
- private final byte[] mPubKey;
- public WrappedPublicKeyRing(byte[] blob, boolean hasAnySecret, int verified) {
- super(hasAnySecret, verified);
- mPubKey = blob;
+ CanonicalizedPublicKeyRing(PGPPublicKeyRing ring, int verified) {
+ super(verified);
+ mRing = ring;
}
- PGPPublicKeyRing getRing() {
+ public CanonicalizedPublicKeyRing(byte[] blob, int verified) {
+ super(verified);
if(mRing == null) {
- PGPObjectFactory factory = new PGPObjectFactory(mPubKey);
- PGPKeyRing keyRing = null;
+ // get first object in block
+ PGPObjectFactory factory = new PGPObjectFactory(blob);
try {
- if ((keyRing = (PGPKeyRing) factory.nextObject()) == null) {
- Log.e(Constants.TAG, "No keys given!");
+ Object obj = factory.nextObject();
+ if (! (obj instanceof PGPPublicKeyRing)) {
+ throw new RuntimeException("Error constructing CanonicalizedPublicKeyRing, should never happen!");
+ }
+ mRing = (PGPPublicKeyRing) obj;
+ if (factory.nextObject() != null) {
+ throw new RuntimeException("Encountered trailing data after keyring, should never happen!");
}
} catch (IOException e) {
- Log.e(Constants.TAG, "Error while converting to PGPKeyRing!", e);
+ throw new RuntimeException("IO Error constructing CanonicalizedPublicKeyRing, should never happen!");
}
-
- mRing = (PGPPublicKeyRing) keyRing;
}
+ }
+
+ PGPPublicKeyRing getRing() {
return mRing;
}
@@ -44,19 +47,11 @@ public class WrappedPublicKeyRing extends WrappedKeyRing {
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 {
+ CanonicalizedPublicKey getEncryptionSubKey() throws PgpGeneralException {
PGPPublicKey key = getRing().getPublicKey(getEncryptId());
if(key != null) {
- WrappedPublicKey cKey = new WrappedPublicKey(this, key);
+ CanonicalizedPublicKey cKey = new CanonicalizedPublicKey(this, key);
if(!cKey.canEncrypt()) {
throw new PgpGeneralException("key error");
}
@@ -65,18 +60,18 @@ public class WrappedPublicKeyRing extends WrappedKeyRing {
throw new PgpGeneralException("no encryption key available");
}
- public IterableIterator<WrappedPublicKey> publicKeyIterator() {
+ public IterableIterator<CanonicalizedPublicKey> publicKeyIterator() {
@SuppressWarnings("unchecked")
final Iterator<PGPPublicKey> it = getRing().getPublicKeys();
- return new IterableIterator<WrappedPublicKey>(new Iterator<WrappedPublicKey>() {
+ return new IterableIterator<CanonicalizedPublicKey>(new Iterator<CanonicalizedPublicKey>() {
@Override
public boolean hasNext() {
return it.hasNext();
}
@Override
- public WrappedPublicKey next() {
- return new WrappedPublicKey(WrappedPublicKeyRing.this, it.next());
+ public CanonicalizedPublicKey next() {
+ return new CanonicalizedPublicKey(CanonicalizedPublicKeyRing.this, it.next());
}
@Override
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java
index ef8044a9b..2eb517697 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKey.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java
@@ -37,26 +37,18 @@ import java.util.List;
* properly imported secret keys only.
*
*/
-public class WrappedSecretKey extends WrappedPublicKey {
+public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
private final PGPSecretKey mSecretKey;
private PGPPrivateKey mPrivateKey = null;
- WrappedSecretKey(WrappedSecretKeyRing ring, PGPSecretKey key) {
+ CanonicalizedSecretKey(CanonicalizedSecretKeyRing 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 CanonicalizedSecretKeyRing getRing() {
+ return (CanonicalizedSecretKeyRing) mRing;
}
public boolean unlock(String passphrase) throws PgpGeneralException {
@@ -97,7 +89,7 @@ public class WrappedSecretKey extends WrappedPublicKey {
signatureGenerator.init(signatureType, mPrivateKey);
PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
- spGen.setSignerUserID(false, mRing.getPrimaryUserId());
+ spGen.setSignerUserID(false, mRing.getPrimaryUserIdWithFallback());
signatureGenerator.setHashedSubpackets(spGen.generate());
return signatureGenerator;
} catch(PGPException e) {
@@ -148,7 +140,7 @@ public class WrappedSecretKey extends WrappedPublicKey {
* @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)
+ public UncachedKeyRing certifyUserIds(CanonicalizedPublicKeyRing publicKeyRing, List<String> userIds)
throws PgpGeneralMsgIdException, NoSuchAlgorithmException, NoSuchProviderException,
PGPException, SignatureException {
@@ -175,7 +167,7 @@ public class WrappedSecretKey extends WrappedPublicKey {
}
// get the master subkey (which we certify for)
- PGPPublicKey publicKey = publicKeyRing.getSubkey().getPublicKey();
+ PGPPublicKey publicKey = publicKeyRing.getPublicKey().getPublicKey();
// fetch public key ring, add the certification and return it
for (String userId : new IterableIterator<String>(userIds.iterator())) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKeyRing.java
index c737b7c46..e48fe5020 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKeyRing.java
@@ -1,10 +1,12 @@
package org.sufficientlysecure.keychain.pgp;
+import org.spongycastle.bcpg.S2K;
import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPObjectFactory;
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.operator.PBESecretKeyDecryptor;
@@ -15,15 +17,21 @@ import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
import java.io.IOException;
+import java.util.HashSet;
import java.util.Iterator;
-public class WrappedSecretKeyRing extends WrappedKeyRing {
+public class CanonicalizedSecretKeyRing extends CanonicalizedKeyRing {
private PGPSecretKeyRing mRing;
- public WrappedSecretKeyRing(byte[] blob, boolean isRevoked, int verified)
+ CanonicalizedSecretKeyRing(PGPSecretKeyRing ring, int verified) {
+ super(verified);
+ mRing = ring;
+ }
+
+ public CanonicalizedSecretKeyRing(byte[] blob, boolean isRevoked, int verified)
{
- super(isRevoked, verified);
+ super(verified);
PGPObjectFactory factory = new PGPObjectFactory(blob);
PGPKeyRing keyRing = null;
try {
@@ -41,19 +49,32 @@ public class WrappedSecretKeyRing extends WrappedKeyRing {
return mRing;
}
- public WrappedSecretKey getSubKey() {
- return new WrappedSecretKey(this, mRing.getSecretKey());
+ public CanonicalizedSecretKey getSecretKey() {
+ return new CanonicalizedSecretKey(this, mRing.getSecretKey());
+ }
+
+ public CanonicalizedSecretKey getSecretKey(long id) {
+ return new CanonicalizedSecretKey(this, mRing.getSecretKey(id));
}
- public WrappedSecretKey getSubKey(long id) {
- return new WrappedSecretKey(this, mRing.getSecretKey(id));
+ public HashSet<Long> getAvailableSubkeys() {
+ HashSet<Long> result = new HashSet<Long>();
+ // then, mark exactly the keys we have available
+ for (PGPSecretKey sub : new IterableIterator<PGPSecretKey>(getRing().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;
}
/** Getter that returns the subkey that should be used for signing. */
- WrappedSecretKey getSigningSubKey() throws PgpGeneralException {
+ CanonicalizedSecretKey getSigningSubKey() throws PgpGeneralException {
PGPSecretKey key = mRing.getSecretKey(getSignId());
if(key != null) {
- WrappedSecretKey cKey = new WrappedSecretKey(this, key);
+ CanonicalizedSecretKey cKey = new CanonicalizedSecretKey(this, key);
if(!cKey.canSign()) {
throw new PgpGeneralException("key error");
}
@@ -88,17 +109,17 @@ public class WrappedSecretKeyRing extends WrappedKeyRing {
}
}
- public IterableIterator<WrappedSecretKey> secretKeyIterator() {
+ public IterableIterator<CanonicalizedSecretKey> secretKeyIterator() {
final Iterator<PGPSecretKey> it = mRing.getSecretKeys();
- return new IterableIterator<WrappedSecretKey>(new Iterator<WrappedSecretKey>() {
+ return new IterableIterator<CanonicalizedSecretKey>(new Iterator<CanonicalizedSecretKey>() {
@Override
public boolean hasNext() {
return it.hasNext();
}
@Override
- public WrappedSecretKey next() {
- return new WrappedSecretKey(WrappedSecretKeyRing.this, it.next());
+ public CanonicalizedSecretKey next() {
+ return new CanonicalizedSecretKey(CanonicalizedSecretKeyRing.this, it.next());
}
@Override
@@ -108,17 +129,17 @@ public class WrappedSecretKeyRing extends WrappedKeyRing {
});
}
- public IterableIterator<WrappedPublicKey> publicKeyIterator() {
+ public IterableIterator<CanonicalizedPublicKey> publicKeyIterator() {
final Iterator<PGPPublicKey> it = getRing().getPublicKeys();
- return new IterableIterator<WrappedPublicKey>(new Iterator<WrappedPublicKey>() {
+ return new IterableIterator<CanonicalizedPublicKey>(new Iterator<CanonicalizedPublicKey>() {
@Override
public boolean hasNext() {
return it.hasNext();
}
@Override
- public WrappedPublicKey next() {
- return new WrappedPublicKey(WrappedSecretKeyRing.this, it.next());
+ public CanonicalizedPublicKey next() {
+ return new CanonicalizedPublicKey(CanonicalizedSecretKeyRing.this, it.next());
}
@Override
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java
index 47b827677..ebc49ab05 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java
@@ -12,7 +12,7 @@ import java.util.regex.Pattern;
* keyring should in all cases agree on the output of all methods described
* here.
*
- * @see org.sufficientlysecure.keychain.pgp.WrappedKeyRing
+ * @see CanonicalizedKeyRing
* @see org.sufficientlysecure.keychain.provider.CachedPublicKeyRing
*
*/
@@ -22,8 +22,10 @@ public abstract class KeyRing {
abstract public String getPrimaryUserId() throws PgpGeneralException;
- public String[] getSplitPrimaryUserId() throws PgpGeneralException {
- return splitUserId(getPrimaryUserId());
+ abstract public String getPrimaryUserIdWithFallback() throws PgpGeneralException;
+
+ public String[] getSplitPrimaryUserIdWithFallback() throws PgpGeneralException {
+ return splitUserId(getPrimaryUserIdWithFallback());
}
abstract public boolean isRevoked() throws PgpGeneralException;
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 a5ccfbd3b..7f2d971ed 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
@@ -231,7 +231,7 @@ public class PgpDecryptVerify {
PGPPublicKeyEncryptedData encryptedDataAsymmetric = null;
PGPPBEEncryptedData encryptedDataSymmetric = null;
- WrappedSecretKey secretEncryptionKey = null;
+ CanonicalizedSecretKey secretEncryptionKey = null;
Iterator<?> it = enc.getEncryptedDataObjects();
boolean asymmetricPacketFound = false;
boolean symmetricPacketFound = false;
@@ -243,10 +243,10 @@ public class PgpDecryptVerify {
PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) obj;
- WrappedSecretKeyRing secretKeyRing;
+ CanonicalizedSecretKeyRing secretKeyRing;
try {
// get actual keyring object based on master key id
- secretKeyRing = mProviderHelper.getWrappedSecretKeyRing(
+ secretKeyRing = mProviderHelper.getCanonicalizedSecretKeyRing(
KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(encData.getKeyID())
);
} catch (ProviderHelper.NotFoundException e) {
@@ -258,7 +258,7 @@ public class PgpDecryptVerify {
continue;
}
// get subkey which has been used for this encryption packet
- secretEncryptionKey = secretKeyRing.getSubKey(encData.getKeyID());
+ secretEncryptionKey = secretKeyRing.getSecretKey(encData.getKeyID());
if (secretEncryptionKey == null) {
// continue with the next packet in the while loop
continue;
@@ -365,8 +365,8 @@ public class PgpDecryptVerify {
Object dataChunk = plainFact.nextObject();
OpenPgpSignatureResultBuilder signatureResultBuilder = new OpenPgpSignatureResultBuilder();
int signatureIndex = -1;
- WrappedPublicKeyRing signingRing = null;
- WrappedPublicKey signingKey = null;
+ CanonicalizedPublicKeyRing signingRing = null;
+ CanonicalizedPublicKey signingKey = null;
if (dataChunk instanceof PGPCompressedData) {
updateProgress(R.string.progress_decompressing_data, currentProgress, 100);
@@ -390,10 +390,10 @@ public class PgpDecryptVerify {
for (int i = 0; i < sigList.size(); ++i) {
try {
long sigKeyId = sigList.get(i).getKeyID();
- signingRing = mProviderHelper.getWrappedPublicKeyRing(
+ signingRing = mProviderHelper.getCanonicalizedPublicKeyRing(
KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(sigKeyId)
);
- signingKey = signingRing.getSubkey(sigKeyId);
+ signingKey = signingRing.getPublicKey(sigKeyId);
signatureIndex = i;
} catch (ProviderHelper.NotFoundException e) {
Log.d(Constants.TAG, "key not found!");
@@ -409,7 +409,7 @@ public class PgpDecryptVerify {
signatureResultBuilder.knownKey(true);
signatureResultBuilder.keyId(signingRing.getMasterKeyId());
try {
- signatureResultBuilder.userId(signingRing.getPrimaryUserId());
+ signatureResultBuilder.userId(signingRing.getPrimaryUserIdWithFallback());
} catch(PgpGeneralException e) {
Log.d(Constants.TAG, "No primary user id in key " + signingRing.getMasterKeyId());
}
@@ -566,8 +566,8 @@ public class PgpDecryptVerify {
throw new InvalidDataException();
}
- WrappedPublicKeyRing signingRing = null;
- WrappedPublicKey signingKey = null;
+ CanonicalizedPublicKeyRing signingRing = null;
+ CanonicalizedPublicKey signingKey = null;
int signatureIndex = -1;
// go through all signatures
@@ -575,10 +575,10 @@ public class PgpDecryptVerify {
for (int i = 0; i < sigList.size(); ++i) {
try {
long sigKeyId = sigList.get(i).getKeyID();
- signingRing = mProviderHelper.getWrappedPublicKeyRing(
+ signingRing = mProviderHelper.getCanonicalizedPublicKeyRing(
KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(sigKeyId)
);
- signingKey = signingRing.getSubkey(sigKeyId);
+ signingKey = signingRing.getPublicKey(sigKeyId);
signatureIndex = i;
} catch (ProviderHelper.NotFoundException e) {
Log.d(Constants.TAG, "key not found!");
@@ -596,7 +596,7 @@ public class PgpDecryptVerify {
signatureResultBuilder.knownKey(true);
signatureResultBuilder.keyId(signingRing.getMasterKeyId());
try {
- signatureResultBuilder.userId(signingRing.getPrimaryUserId());
+ signatureResultBuilder.userId(signingRing.getPrimaryUserIdWithFallback());
} catch(PgpGeneralException e) {
Log.d(Constants.TAG, "No primary user id in key " + signingRing.getMasterKeyId());
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java
index 969ff1de8..0ceefc4d9 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java
@@ -24,6 +24,7 @@ import android.content.pm.PackageManager.NameNotFoundException;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.helper.Preferences;
import org.sufficientlysecure.keychain.util.Log;
import java.io.File;
@@ -60,7 +61,11 @@ public class PgpHelper {
}
public static String getFullVersion(Context context) {
- return "OpenKeychain v" + getVersion(context);
+ if(Preferences.getPreferences(context).getConcealPgpApplication()){
+ return "";
+ } else {
+ return "OpenKeychain v" + getVersion(context);
+ }
}
// public static final class content {
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 6fc55cfb8..846b00ef2 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java
@@ -34,7 +34,7 @@ import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog;
-import org.sufficientlysecure.keychain.service.OperationResults.ImportResult;
+import org.sufficientlysecure.keychain.service.OperationResults.ImportKeyResult;
import org.sufficientlysecure.keychain.service.OperationResults.SaveKeyringResult;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ProgressScaler;
@@ -93,7 +93,7 @@ public class PgpImportExport {
}
}
- public boolean uploadKeyRingToServer(HkpKeyserver server, WrappedPublicKeyRing keyring) {
+ public boolean uploadKeyRingToServer(HkpKeyserver server, CanonicalizedPublicKeyRing keyring) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ArmoredOutputStream aos = null;
try {
@@ -123,14 +123,14 @@ public class PgpImportExport {
}
/** Imports keys from given data. If keyIds is given only those are imported */
- public ImportResult importKeyRings(List<ParcelableKeyRing> entries) {
+ public ImportKeyResult importKeyRings(List<ParcelableKeyRing> entries) {
updateProgress(R.string.progress_importing, 0, 100);
// If there aren't even any keys, do nothing here.
if (entries == null || entries.size() == 0) {
- return new ImportResult(
- ImportResult.RESULT_FAIL_NOTHING, mProviderHelper.getLog(), 0, 0, 0);
+ return new ImportKeyResult(
+ ImportKeyResult.RESULT_FAIL_NOTHING, mProviderHelper.getLog(), 0, 0, 0);
}
int newKeys = 0, oldKeys = 0, badKeys = 0;
@@ -185,26 +185,26 @@ public class PgpImportExport {
int resultType = 0;
// special return case: no new keys at all
if (badKeys == 0 && newKeys == 0 && oldKeys == 0) {
- resultType = ImportResult.RESULT_FAIL_NOTHING;
+ resultType = ImportKeyResult.RESULT_FAIL_NOTHING;
} else {
if (newKeys > 0) {
- resultType |= ImportResult.RESULT_OK_NEWKEYS;
+ resultType |= ImportKeyResult.RESULT_OK_NEWKEYS;
}
if (oldKeys > 0) {
- resultType |= ImportResult.RESULT_OK_UPDATED;
+ resultType |= ImportKeyResult.RESULT_OK_UPDATED;
}
if (badKeys > 0) {
- resultType |= ImportResult.RESULT_WITH_ERRORS;
+ resultType |= ImportKeyResult.RESULT_WITH_ERRORS;
if (newKeys == 0 && oldKeys == 0) {
- resultType |= ImportResult.RESULT_ERROR;
+ resultType |= ImportKeyResult.RESULT_ERROR;
}
}
if (log.containsWarnings()) {
- resultType |= ImportResult.RESULT_WITH_WARNINGS;
+ resultType |= ImportKeyResult.RESULT_WITH_WARNINGS;
}
}
- return new ImportResult(resultType, log, newKeys, oldKeys, badKeys);
+ return new ImportKeyResult(resultType, log, newKeys, oldKeys, badKeys);
}
@@ -235,7 +235,7 @@ public class PgpImportExport {
updateProgress(progress * 100 / masterKeyIdsSize, 100);
try {
- WrappedPublicKeyRing ring = mProviderHelper.getWrappedPublicKeyRing(
+ CanonicalizedPublicKeyRing ring = mProviderHelper.getCanonicalizedPublicKeyRing(
KeychainContract.KeyRings.buildUnifiedKeyRingUri(pubKeyMasterId)
);
@@ -263,8 +263,8 @@ public class PgpImportExport {
updateProgress(progress * 100 / masterKeyIdsSize, 100);
try {
- WrappedSecretKeyRing secretKeyRing =
- mProviderHelper.getWrappedSecretKeyRing(secretKeyMasterId);
+ CanonicalizedSecretKeyRing secretKeyRing =
+ mProviderHelper.getCanonicalizedSecretKeyRing(secretKeyMasterId);
secretKeyRing.encode(arOutStream);
} catch (ProviderHelper.NotFoundException e) {
Log.e(Constants.TAG, "key not found!", e);
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 9a08290e4..b1bd6a77a 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
@@ -46,15 +46,17 @@ 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.exception.PgpGeneralMsgIdException;
+import org.sufficientlysecure.keychain.service.OperationResultParcel;
import org.sufficientlysecure.keychain.service.OperationResultParcel.LogLevel;
import org.sufficientlysecure.keychain.service.OperationResultParcel.LogType;
import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog;
+import org.sufficientlysecure.keychain.service.OperationResults.EditKeyResult;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyAdd;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Primes;
+import org.sufficientlysecure.keychain.util.ProgressScaler;
import java.io.IOException;
import java.math.BigInteger;
@@ -65,10 +67,9 @@ import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.SignatureException;
import java.util.Arrays;
-import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
-import java.util.TimeZone;
+import java.util.Stack;
/**
* This class is the single place where ALL operations that actually modify a PGP public or secret
@@ -80,7 +81,7 @@ import java.util.TimeZone;
* This indicator may be null.
*/
public class PgpKeyOperation {
- private Progressable mProgress;
+ private Stack<Progressable> mProgress;
private static final int[] PREFERRED_SYMMETRIC_ALGORITHMS = new int[]{
SymmetricKeyAlgorithmTags.AES_256, SymmetricKeyAlgorithmTags.AES_192,
@@ -94,21 +95,43 @@ public class PgpKeyOperation {
public PgpKeyOperation(Progressable progress) {
super();
- this.mProgress = progress;
+ if (progress != null) {
+ mProgress = new Stack<Progressable>();
+ mProgress.push(progress);
+ }
+ }
+
+ private void subProgressPush(int from, int to) {
+ if (mProgress == null) {
+ return;
+ }
+ mProgress.push(new ProgressScaler(mProgress.peek(), from, to, 100));
+ }
+ private void subProgressPop() {
+ if (mProgress == null) {
+ return;
+ }
+ if (mProgress.size() == 1) {
+ throw new RuntimeException("Tried to pop progressable without prior push! "
+ + "This is a programming error, please file a bug report.");
+ }
+ mProgress.pop();
}
- void updateProgress(int message, int current, int total) {
- if (mProgress != null) {
- mProgress.setProgress(message, current, total);
+ private void progress(int message, int current) {
+ if (mProgress == null) {
+ return;
}
+ mProgress.peek().setProgress(message, current, 100);
}
/** Creates new secret key. */
- private PGPKeyPair createKey(int algorithmChoice, int keySize) throws PgpGeneralMsgIdException {
+ private PGPKeyPair createKey(int algorithmChoice, int keySize, OperationLog log, int indent) {
try {
if (keySize < 512) {
- throw new PgpGeneralMsgIdException(R.string.error_key_size_minimum512bit);
+ log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_KEYSIZE_512, indent);
+ return null;
}
int algorithm;
@@ -116,6 +139,7 @@ public class PgpKeyOperation {
switch (algorithmChoice) {
case Constants.choice.algorithm.dsa: {
+ progress(R.string.progress_generating_dsa, 30);
keyGen = KeyPairGenerator.getInstance("DSA", Constants.BOUNCY_CASTLE_PROVIDER_NAME);
keyGen.initialize(keySize, new SecureRandom());
algorithm = PGPPublicKey.DSA;
@@ -123,6 +147,7 @@ public class PgpKeyOperation {
}
case Constants.choice.algorithm.elgamal: {
+ progress(R.string.progress_generating_elgamal, 30);
keyGen = KeyPairGenerator.getInstance("ElGamal", Constants.BOUNCY_CASTLE_PROVIDER_NAME);
BigInteger p = Primes.getBestPrime(keySize);
BigInteger g = new BigInteger("2");
@@ -135,6 +160,7 @@ public class PgpKeyOperation {
}
case Constants.choice.algorithm.rsa: {
+ progress(R.string.progress_generating_rsa, 30);
keyGen = KeyPairGenerator.getInstance("RSA", Constants.BOUNCY_CASTLE_PROVIDER_NAME);
keyGen.initialize(keySize, new SecureRandom());
@@ -143,7 +169,8 @@ public class PgpKeyOperation {
}
default: {
- throw new PgpGeneralMsgIdException(R.string.error_unknown_algorithm_choice);
+ log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_UNKNOWN_ALGO, indent);
+ return null;
}
}
@@ -157,31 +184,55 @@ public class PgpKeyOperation {
} catch(InvalidAlgorithmParameterException e) {
throw new RuntimeException(e);
} catch(PGPException e) {
- throw new PgpGeneralMsgIdException(R.string.msg_mf_error_pgp, e);
+ Log.e(Constants.TAG, "internal pgp error", e);
+ log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_INTERNAL_PGP, indent);
+ return null;
}
}
- public UncachedKeyRing createSecretKeyRing(SaveKeyringParcel saveParcel, OperationLog log,
- int indent) {
+ public EditKeyResult createSecretKeyRing(SaveKeyringParcel saveParcel) {
+
+ OperationLog log = new OperationLog();
+ int indent = 0;
try {
- log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_KEYID, indent);
+ log.add(LogLevel.START, LogType.MSG_CR, indent);
+ progress(R.string.progress_building_key, 0);
indent += 1;
- updateProgress(R.string.progress_building_key, 0, 100);
- if (saveParcel.addSubKeys == null || saveParcel.addSubKeys.isEmpty()) {
+ if (saveParcel.mAddSubKeys.isEmpty()) {
log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_NO_MASTER, indent);
- return null;
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ }
+
+ if (saveParcel.mAddUserIds.isEmpty()) {
+ log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_NO_USER_ID, indent);
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
- SubkeyAdd add = saveParcel.addSubKeys.remove(0);
- PGPKeyPair keyPair = createKey(add.mAlgorithm, add.mKeysize);
+ SubkeyAdd add = saveParcel.mAddSubKeys.remove(0);
+ if ((add.mFlags & KeyFlags.CERTIFY_OTHER) != KeyFlags.CERTIFY_OTHER) {
+ log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_NO_CERTIFY, indent);
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ }
if (add.mAlgorithm == Constants.choice.algorithm.elgamal) {
- throw new PgpGeneralMsgIdException(R.string.error_master_key_must_not_be_el_gamal);
+ log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_MASTER_ELGAMAL, indent);
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
+ subProgressPush(10, 30);
+ PGPKeyPair keyPair = createKey(add.mAlgorithm, add.mKeysize, log, indent);
+ subProgressPop();
+
+ // return null if this failed (an error will already have been logged by createKey)
+ if (keyPair == null) {
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ }
+
+ progress(R.string.progress_building_master_key, 40);
+
// define hashing and signing algos
PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder()
.build().get(HashAlgorithmTags.SHA1);
@@ -195,17 +246,16 @@ public class PgpKeyOperation {
PGPSecretKeyRing sKR = new PGPSecretKeyRing(
masterSecretKey.getEncoded(), new JcaKeyFingerprintCalculator());
- return internal(sKR, masterSecretKey, saveParcel, "", log, indent);
+ subProgressPush(50, 100);
+ return internal(sKR, masterSecretKey, add.mFlags, saveParcel, "", log);
} catch (PGPException e) {
+ log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_INTERNAL_PGP, indent);
Log.e(Constants.TAG, "pgp error encoding key", e);
- return null;
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
} catch (IOException e) {
Log.e(Constants.TAG, "io error encoding key", e);
- return null;
- } catch (PgpGeneralMsgIdException e) {
- Log.e(Constants.TAG, "pgp msg id error", e);
- return null;
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
}
@@ -221,8 +271,11 @@ public class PgpKeyOperation {
* are changed by adding new certificates, which implicitly override older certificates.
*
*/
- public UncachedKeyRing modifySecretKeyRing(WrappedSecretKeyRing wsKR, SaveKeyringParcel saveParcel,
- String passphrase, OperationLog log, int indent) {
+ public EditKeyResult modifySecretKeyRing(CanonicalizedSecretKeyRing wsKR, SaveKeyringParcel saveParcel,
+ String passphrase) {
+
+ OperationLog log = new OperationLog();
+ int indent = 0;
/*
* 1. Unlock private key
@@ -235,14 +288,15 @@ public class PgpKeyOperation {
* 6. If requested, change passphrase
*/
- log.add(LogLevel.START, LogType.MSG_MF, indent);
+ log.add(LogLevel.START, LogType.MSG_MF, indent,
+ PgpKeyHelper.convertKeyIdToHex(wsKR.getMasterKeyId()));
indent += 1;
- updateProgress(R.string.progress_building_key, 0, 100);
+ progress(R.string.progress_building_key, 0);
// Make sure this is called with a proper SaveKeyringParcel
if (saveParcel.mMasterKeyId == null || saveParcel.mMasterKeyId != wsKR.getMasterKeyId()) {
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_KEYID, indent);
- return null;
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
// We work on bouncycastle object level here
@@ -254,22 +308,30 @@ public class PgpKeyOperation {
|| !Arrays.equals(saveParcel.mFingerprint,
masterSecretKey.getPublicKey().getFingerprint())) {
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_FINGERPRINT, indent);
- return null;
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
- return internal(sKR, masterSecretKey, saveParcel, passphrase, log, indent);
+ // read masterKeyFlags, and use the same as before.
+ // since this is the master key, this contains at least CERTIFY_OTHER
+ int masterKeyFlags = readKeyFlags(masterSecretKey.getPublicKey()) | KeyFlags.CERTIFY_OTHER;
+
+ return internal(sKR, masterSecretKey, masterKeyFlags, saveParcel, passphrase, log);
}
- private UncachedKeyRing internal(PGPSecretKeyRing sKR, PGPSecretKey masterSecretKey,
+ private EditKeyResult internal(PGPSecretKeyRing sKR, PGPSecretKey masterSecretKey,
+ int masterKeyFlags,
SaveKeyringParcel saveParcel, String passphrase,
- OperationLog log, int indent) {
+ OperationLog log) {
+
+ int indent = 1;
- updateProgress(R.string.progress_certifying_master_key, 20, 100);
+ progress(R.string.progress_modify, 0);
PGPPublicKey masterPublicKey = masterSecretKey.getPublicKey();
// 1. Unlock private key
+ progress(R.string.progress_modify_unlock, 10);
log.add(LogLevel.DEBUG, LogType.MSG_MF_UNLOCK, indent);
PGPPrivateKey masterPrivateKey;
{
@@ -279,7 +341,7 @@ public class PgpKeyOperation {
masterPrivateKey = masterSecretKey.extractPrivateKey(keyDecryptor);
} catch (PGPException e) {
log.add(LogLevel.ERROR, LogType.MSG_MF_UNLOCK_ERROR, indent + 1);
- return null;
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
}
@@ -289,18 +351,28 @@ public class PgpKeyOperation {
PGPPublicKey modifiedPublicKey = masterPublicKey;
// 2a. Add certificates for new user ids
- for (String userId : saveParcel.addUserIds) {
+ subProgressPush(15, 25);
+ for (int i = 0; i < saveParcel.mAddUserIds.size(); i++) {
+
+ progress(R.string.progress_modify_adduid, (i-1) * (100 / saveParcel.mAddSubKeys.size()));
+ String userId = saveParcel.mAddUserIds.get(i);
log.add(LogLevel.INFO, LogType.MSG_MF_UID_ADD, indent);
+ if (userId.equals("")) {
+ log.add(LogLevel.ERROR, LogType.MSG_MF_UID_ERROR_EMPTY, indent+1);
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ }
+
// this operation supersedes all previous binding and revocation certificates,
// so remove those to retain assertions from canonicalization for later operations
@SuppressWarnings("unchecked")
Iterator<PGPSignature> it = modifiedPublicKey.getSignaturesForID(userId);
if (it != null) {
for (PGPSignature cert : new IterableIterator<PGPSignature>(it)) {
- // if it's not a self cert, never mind
if (cert.getKeyID() != masterPublicKey.getKeyID()) {
- continue;
+ // foreign certificate?! error error error
+ log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_INTEGRITY, indent);
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
if (cert.getSignatureType() == PGPSignature.CERTIFICATION_REVOCATION
|| cert.getSignatureType() == PGPSignature.NO_CERTIFICATION
@@ -314,27 +386,39 @@ public class PgpKeyOperation {
}
// if it's supposed to be primary, we can do that here as well
- boolean isPrimary = saveParcel.changePrimaryUserId != null
- && userId.equals(saveParcel.changePrimaryUserId);
+ boolean isPrimary = saveParcel.mChangePrimaryUserId != null
+ && userId.equals(saveParcel.mChangePrimaryUserId);
// generate and add new certificate
PGPSignature cert = generateUserIdSignature(masterPrivateKey,
- masterPublicKey, userId, isPrimary);
- modifiedPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, cert);
+ masterPublicKey, userId, isPrimary, masterKeyFlags);
+ modifiedPublicKey = PGPPublicKey.addCertification(modifiedPublicKey, userId, cert);
}
+ subProgressPop();
// 2b. Add revocations for revoked user ids
- for (String userId : saveParcel.revokeUserIds) {
- log.add(LogLevel.INFO, LogType.MSG_MF_UID_REVOKE, indent);
- // a duplicate revocatin will be removed during canonicalization, so no need to
+ subProgressPush(25, 40);
+ for (int i = 0; i < saveParcel.mRevokeUserIds.size(); i++) {
+
+ progress(R.string.progress_modify_revokeuid, (i-1) * (100 / saveParcel.mAddSubKeys.size()));
+ String userId = saveParcel.mRevokeUserIds.get(i);
+ log.add(LogLevel.INFO, LogType.MSG_MF_UID_REVOKE, indent, userId);
+
+ // a duplicate revocation will be removed during canonicalization, so no need to
// take care of that here.
PGPSignature cert = generateRevocationSignature(masterPrivateKey,
masterPublicKey, userId);
- modifiedPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, cert);
+ modifiedPublicKey = PGPPublicKey.addCertification(modifiedPublicKey, userId, cert);
}
+ subProgressPop();
// 3. If primary user id changed, generate new certificates for both old and new
- if (saveParcel.changePrimaryUserId != null) {
+ if (saveParcel.mChangePrimaryUserId != null) {
+ progress(R.string.progress_modify_primaryuid, 40);
+
+ // keep track if we actually changed one
+ boolean ok = false;
log.add(LogLevel.INFO, LogType.MSG_MF_UID_PRIMARY, indent);
+ indent += 1;
// we work on the modifiedPublicKey here, to respect new or newly revoked uids
// noinspection unchecked
@@ -343,10 +427,11 @@ public class PgpKeyOperation {
PGPSignature currentCert = null;
// noinspection unchecked
for (PGPSignature cert : new IterableIterator<PGPSignature>(
- masterPublicKey.getSignaturesForID(userId))) {
- // if it's not a self cert, never mind
+ modifiedPublicKey.getSignaturesForID(userId))) {
if (cert.getKeyID() != masterPublicKey.getKeyID()) {
- continue;
+ // foreign certificate?! error error error
+ log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_INTEGRITY, indent);
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
// we know from canonicalization that if there is any revocation here, it
// is valid and not superseded by a newer certification.
@@ -367,30 +452,33 @@ public class PgpKeyOperation {
if (currentCert == null) {
// no certificate found?! error error error
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_INTEGRITY, indent);
- return null;
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
// we definitely should not update certifications of revoked keys, so just leave it.
if (isRevoked) {
// revoked user ids cannot be primary!
- if (userId.equals(saveParcel.changePrimaryUserId)) {
+ if (userId.equals(saveParcel.mChangePrimaryUserId)) {
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_REVOKED_PRIMARY, indent);
- return null;
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
continue;
}
// if this is~ the/a primary user id
- if (currentCert.hasSubpackets() && currentCert.getHashedSubPackets().isPrimaryUserID()) {
+ if (currentCert.getHashedSubPackets() != null
+ && currentCert.getHashedSubPackets().isPrimaryUserID()) {
// if it's the one we want, just leave it as is
- if (userId.equals(saveParcel.changePrimaryUserId)) {
+ if (userId.equals(saveParcel.mChangePrimaryUserId)) {
+ ok = true;
continue;
}
// otherwise, generate new non-primary certification
+ log.add(LogLevel.DEBUG, LogType.MSG_MF_PRIMARY_REPLACE_OLD, indent);
modifiedPublicKey = PGPPublicKey.removeCertification(
modifiedPublicKey, userId, currentCert);
PGPSignature newCert = generateUserIdSignature(
- masterPrivateKey, masterPublicKey, userId, false);
+ masterPrivateKey, masterPublicKey, userId, false, masterKeyFlags);
modifiedPublicKey = PGPPublicKey.addCertification(
modifiedPublicKey, userId, newCert);
continue;
@@ -399,20 +487,28 @@ public class PgpKeyOperation {
// if we are here, this is not currently a primary user id
// if it should be
- if (userId.equals(saveParcel.changePrimaryUserId)) {
+ if (userId.equals(saveParcel.mChangePrimaryUserId)) {
// add shiny new primary user id certificate
+ log.add(LogLevel.DEBUG, LogType.MSG_MF_PRIMARY_NEW, indent);
modifiedPublicKey = PGPPublicKey.removeCertification(
modifiedPublicKey, userId, currentCert);
PGPSignature newCert = generateUserIdSignature(
- masterPrivateKey, masterPublicKey, userId, true);
+ masterPrivateKey, masterPublicKey, userId, true, masterKeyFlags);
modifiedPublicKey = PGPPublicKey.addCertification(
modifiedPublicKey, userId, newCert);
+ ok = true;
}
// user id is not primary and is not supposed to be - nothing to do here.
}
+ indent -= 1;
+
+ if (!ok) {
+ log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_NOEXIST_PRIMARY, indent);
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ }
}
// Update the secret key ring
@@ -423,40 +519,80 @@ public class PgpKeyOperation {
}
// 4a. For each subkey change, generate new subkey binding certificate
- for (SaveKeyringParcel.SubkeyChange change : saveParcel.changeSubKeys) {
+ subProgressPush(50, 60);
+ for (int i = 0; i < saveParcel.mChangeSubKeys.size(); i++) {
+
+ progress(R.string.progress_modify_subkeychange, (i-1) * (100 / saveParcel.mAddSubKeys.size()));
+ SaveKeyringParcel.SubkeyChange change = saveParcel.mChangeSubKeys.get(i);
log.add(LogLevel.INFO, LogType.MSG_MF_SUBKEY_CHANGE,
indent, PgpKeyHelper.convertKeyIdToHex(change.mKeyId));
+
+ // TODO allow changes in master key? this implies generating new user id certs...
+ if (change.mKeyId == masterPublicKey.getKeyID()) {
+ Log.e(Constants.TAG, "changing the master key not supported");
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ }
+
PGPSecretKey sKey = sKR.getSecretKey(change.mKeyId);
if (sKey == null) {
log.add(LogLevel.ERROR, LogType.MSG_MF_SUBKEY_MISSING,
indent + 1, PgpKeyHelper.convertKeyIdToHex(change.mKeyId));
- return null;
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
PGPPublicKey pKey = sKey.getPublicKey();
- if (change.mExpiry != null && new Date(change.mExpiry).before(new Date())) {
+ // expiry must not be in the past
+ if (change.mExpiry != null && new Date(change.mExpiry*1000).before(new Date())) {
log.add(LogLevel.ERROR, LogType.MSG_MF_SUBKEY_PAST_EXPIRY,
indent + 1, PgpKeyHelper.convertKeyIdToHex(change.mKeyId));
- return null;
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ }
+
+ // keep old flags, or replace with new ones
+ int flags = change.mFlags == null ? readKeyFlags(pKey) : change.mFlags;
+ long expiry;
+ if (change.mExpiry == null) {
+ long valid = pKey.getValidSeconds();
+ expiry = valid == 0
+ ? 0
+ : pKey.getCreationTime().getTime() / 1000 + pKey.getValidSeconds();
+ } else {
+ expiry = change.mExpiry;
}
- // generate and add new signature. we can be sloppy here and just leave the old one,
- // it will be removed during canonicalization
+ // drop all old signatures, they will be superseded by the new one
+ //noinspection unchecked
+ for (PGPSignature sig : new IterableIterator<PGPSignature>(pKey.getSignatures())) {
+ // special case: if there is a revocation, don't use expiry from before
+ if (change.mExpiry == null
+ && sig.getSignatureType() == PGPSignature.SUBKEY_REVOCATION) {
+ expiry = 0;
+ }
+ pKey = PGPPublicKey.removeCertification(pKey, sig);
+ }
+
+ // generate and add new signature
PGPSignature sig = generateSubkeyBindingSignature(masterPublicKey, masterPrivateKey,
- sKey, pKey, change.mFlags, change.mExpiry, passphrase);
+ sKey, pKey, flags, expiry, passphrase);
pKey = PGPPublicKey.addCertification(pKey, sig);
sKR = PGPSecretKeyRing.insertSecretKey(sKR, PGPSecretKey.replacePublicKey(sKey, pKey));
}
+ subProgressPop();
// 4b. For each subkey revocation, generate new subkey revocation certificate
- for (long revocation : saveParcel.revokeSubKeys) {
+ subProgressPush(60, 70);
+ for (int i = 0; i < saveParcel.mRevokeSubKeys.size(); i++) {
+
+ progress(R.string.progress_modify_subkeyrevoke, (i-1) * (100 / saveParcel.mAddSubKeys.size()));
+ long revocation = saveParcel.mRevokeSubKeys.get(i);
log.add(LogLevel.INFO, LogType.MSG_MF_SUBKEY_REVOKE,
indent, PgpKeyHelper.convertKeyIdToHex(revocation));
+
PGPSecretKey sKey = sKR.getSecretKey(revocation);
if (sKey == null) {
log.add(LogLevel.ERROR, LogType.MSG_MF_SUBKEY_MISSING,
indent+1, PgpKeyHelper.convertKeyIdToHex(revocation));
- return null;
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
PGPPublicKey pKey = sKey.getPublicKey();
@@ -466,54 +602,64 @@ public class PgpKeyOperation {
pKey = PGPPublicKey.addCertification(pKey, sig);
sKR = PGPSecretKeyRing.insertSecretKey(sKR, PGPSecretKey.replacePublicKey(sKey, pKey));
}
+ subProgressPop();
// 5. Generate and add new subkeys
- for (SaveKeyringParcel.SubkeyAdd add : saveParcel.addSubKeys) {
- try {
-
- if (add.mExpiry != null && new Date(add.mExpiry).before(new Date())) {
- log.add(LogLevel.ERROR, LogType.MSG_MF_SUBKEY_PAST_EXPIRY, indent +1);
- return null;
- }
-
- log.add(LogLevel.INFO, LogType.MSG_MF_SUBKEY_NEW, indent);
-
- // generate a new secret key (privkey only for now)
- PGPKeyPair keyPair = createKey(add.mAlgorithm, add.mKeysize);
+ subProgressPush(70, 90);
+ for (int i = 0; i < saveParcel.mAddSubKeys.size(); i++) {
- // add subkey binding signature (making this a sub rather than master key)
- PGPPublicKey pKey = keyPair.getPublicKey();
- PGPSignature cert = generateSubkeyBindingSignature(
- masterPublicKey, masterPrivateKey, keyPair.getPrivateKey(), pKey,
- add.mFlags, add.mExpiry);
- pKey = PGPPublicKey.addSubkeyBindingCertification(pKey, cert);
+ progress(R.string.progress_modify_subkeyadd, (i-1) * (100 / saveParcel.mAddSubKeys.size()));
+ SaveKeyringParcel.SubkeyAdd add = saveParcel.mAddSubKeys.get(0);
+ log.add(LogLevel.INFO, LogType.MSG_MF_SUBKEY_NEW, indent);
- PGPSecretKey sKey; {
- // define hashing and signing algos
- PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder()
- .build().get(HashAlgorithmTags.SHA1);
+ if (add.mExpiry != null && new Date(add.mExpiry*1000).before(new Date())) {
+ log.add(LogLevel.ERROR, LogType.MSG_MF_SUBKEY_PAST_EXPIRY, indent +1);
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ }
- // Build key encrypter and decrypter based on passphrase
- PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder(
- PGPEncryptedData.CAST5, sha1Calc)
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build("".toCharArray());
+ // generate a new secret key (privkey only for now)
+ subProgressPush(
+ (i-1) * (100 / saveParcel.mAddSubKeys.size()),
+ i * (100 / saveParcel.mAddSubKeys.size())
+ );
+ PGPKeyPair keyPair = createKey(add.mAlgorithm, add.mKeysize, log, indent);
+ subProgressPop();
+ if(keyPair == null) {
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ }
- sKey = new PGPSecretKey(keyPair.getPrivateKey(), pKey,
- sha1Calc, false, keyEncryptor);
- }
+ // add subkey binding signature (making this a sub rather than master key)
+ PGPPublicKey pKey = keyPair.getPublicKey();
+ PGPSignature cert = generateSubkeyBindingSignature(
+ masterPublicKey, masterPrivateKey, keyPair.getPrivateKey(), pKey,
+ add.mFlags, add.mExpiry == null ? 0 : add.mExpiry);
+ pKey = PGPPublicKey.addSubkeyBindingCertification(pKey, cert);
+
+ PGPSecretKey sKey; {
+ // define hashing and signing algos
+ PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder()
+ .build().get(HashAlgorithmTags.SHA1);
+
+ // Build key encrypter and decrypter based on passphrase
+ PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder(
+ PGPEncryptedData.CAST5, sha1Calc)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build("".toCharArray());
+
+ sKey = new PGPSecretKey(keyPair.getPrivateKey(), pKey,
+ sha1Calc, false, keyEncryptor);
+ }
- log.add(LogLevel.DEBUG, LogType.MSG_MF_SUBKEY_NEW_ID,
- indent+1, PgpKeyHelper.convertKeyIdToHex(sKey.getKeyID()));
+ log.add(LogLevel.DEBUG, LogType.MSG_MF_SUBKEY_NEW_ID,
+ indent+1, PgpKeyHelper.convertKeyIdToHex(sKey.getKeyID()));
- sKR = PGPSecretKeyRing.insertSecretKey(sKR, sKey);
+ sKR = PGPSecretKeyRing.insertSecretKey(sKR, sKey);
- } catch (PgpGeneralMsgIdException e) {
- return null;
- }
}
+ subProgressPop();
// 6. If requested, change passphrase
- if (saveParcel.newPassphrase != null) {
+ if (saveParcel.mNewPassphrase != null) {
+ progress(R.string.progress_modify_passphrase, 90);
log.add(LogLevel.INFO, LogType.MSG_MF_PASSPHRASE, indent);
PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build()
.get(HashAlgorithmTags.SHA1);
@@ -523,7 +669,7 @@ public class PgpKeyOperation {
PBESecretKeyEncryptor keyEncryptorNew = new JcePBESecretKeyEncryptorBuilder(
PGPEncryptedData.CAST5, sha1Calc)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
- saveParcel.newPassphrase.toCharArray());
+ saveParcel.mNewPassphrase.toCharArray());
sKR = PGPSecretKeyRing.copyWithNewPassword(sKR, keyDecryptor, keyEncryptorNew);
}
@@ -531,22 +677,24 @@ public class PgpKeyOperation {
// This one must only be thrown by
} catch (IOException e) {
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_ENCODE, indent+1);
- return null;
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
} catch (PGPException e) {
+ Log.e(Constants.TAG, "encountered pgp error while modifying key", e);
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_PGP, indent+1);
- return null;
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
} catch (SignatureException e) {
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_SIG, indent+1);
- return null;
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
+ progress(R.string.progress_done, 100);
log.add(LogLevel.OK, LogType.MSG_MF_SUCCESS, indent);
- return new UncachedKeyRing(sKR);
+ return new EditKeyResult(OperationResultParcel.RESULT_OK, log, new UncachedKeyRing(sKR));
}
private static PGPSignature generateUserIdSignature(
- PGPPrivateKey masterPrivateKey, PGPPublicKey pKey, String userId, boolean primary)
+ PGPPrivateKey masterPrivateKey, PGPPublicKey pKey, String userId, boolean primary, int flags)
throws IOException, PGPException, SignatureException {
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
pKey.getAlgorithm(), PGPUtil.SHA1)
@@ -558,6 +706,7 @@ public class PgpKeyOperation {
subHashedPacketsGen.setPreferredHashAlgorithms(true, PREFERRED_HASH_ALGORITHMS);
subHashedPacketsGen.setPreferredCompressionAlgorithms(true, PREFERRED_COMPRESSION_ALGORITHMS);
subHashedPacketsGen.setPrimaryUserID(false, primary);
+ subHashedPacketsGen.setKeyFlags(false, flags);
sGen.setHashedSubpackets(subHashedPacketsGen.generate());
sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey);
return sGen.generateCertification(userId, pKey);
@@ -599,7 +748,7 @@ public class PgpKeyOperation {
private static PGPSignature generateSubkeyBindingSignature(
PGPPublicKey masterPublicKey, PGPPrivateKey masterPrivateKey,
- PGPSecretKey sKey, PGPPublicKey pKey, int flags, Long expiry, String passphrase)
+ PGPSecretKey sKey, PGPPublicKey pKey, int flags, long expiry, String passphrase)
throws IOException, PGPException, SignatureException {
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
@@ -611,7 +760,7 @@ public class PgpKeyOperation {
private static PGPSignature generateSubkeyBindingSignature(
PGPPublicKey masterPublicKey, PGPPrivateKey masterPrivateKey,
- PGPPrivateKey subPrivateKey, PGPPublicKey pKey, int flags, Long expiry)
+ PGPPrivateKey subPrivateKey, PGPPublicKey pKey, int flags, long expiry)
throws IOException, PGPException, SignatureException {
// date for signing
@@ -640,17 +789,9 @@ public class PgpKeyOperation {
hashedPacketsGen.setKeyFlags(false, flags);
}
- if (expiry != null) {
- Calendar creationDate = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
- creationDate.setTime(pKey.getCreationTime());
-
- // (Just making sure there's no programming error here, this MUST have been checked above!)
- if (new Date(expiry).before(todayDate)) {
- throw new RuntimeException("Bad subkey creation date, this is a bug!");
- }
- hashedPacketsGen.setKeyExpirationTime(false, expiry - creationDate.getTimeInMillis());
- } else {
- hashedPacketsGen.setKeyExpirationTime(false, 0);
+ if (expiry > 0) {
+ long creationTime = pKey.getCreationTime().getTime() / 1000;
+ hashedPacketsGen.setKeyExpirationTime(false, expiry - creationTime);
}
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
@@ -665,4 +806,22 @@ public class PgpKeyOperation {
}
+ /** Returns all flags valid for this key.
+ *
+ * This method does not do any validity checks on the signature, so it should not be used on
+ * a non-canonicalized key!
+ *
+ */
+ private static int readKeyFlags(PGPPublicKey key) {
+ int flags = 0;
+ //noinspection unchecked
+ for(PGPSignature sig : new IterableIterator<PGPSignature>(key.getSignatures())) {
+ if (sig.getHashedSubPackets() == null) {
+ continue;
+ }
+ flags |= sig.getHashedSubPackets().getKeyFlags();
+ }
+ return flags;
+ }
+
}
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 4cb92c368..f0403e625 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java
@@ -266,11 +266,11 @@ public class PgpSignEncrypt {
}
/* Get keys for signature generation for later usage */
- WrappedSecretKey signingKey = null;
+ CanonicalizedSecretKey signingKey = null;
if (enableSignature) {
- WrappedSecretKeyRing signingKeyRing;
+ CanonicalizedSecretKeyRing signingKeyRing;
try {
- signingKeyRing = mProviderHelper.getWrappedSecretKeyRing(mSignatureMasterKeyId);
+ signingKeyRing = mProviderHelper.getCanonicalizedSecretKeyRing(mSignatureMasterKeyId);
} catch (ProviderHelper.NotFoundException e) {
throw new NoSigningKeyException();
}
@@ -316,9 +316,9 @@ public class PgpSignEncrypt {
// Asymmetric encryption
for (long id : mEncryptionMasterKeyIds) {
try {
- WrappedPublicKeyRing keyRing = mProviderHelper.getWrappedPublicKeyRing(
+ CanonicalizedPublicKeyRing keyRing = mProviderHelper.getCanonicalizedPublicKeyRing(
KeyRings.buildUnifiedKeyRingUri(id));
- WrappedPublicKey key = keyRing.getEncryptionSubKey();
+ CanonicalizedPublicKey key = keyRing.getEncryptionSubKey();
cPk.addMethod(key.getPubKeyEncryptionGenerator());
} catch (PgpGeneralException e) {
Log.e(Constants.TAG, "key not found!", e);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
index 441e2762a..502e3a70c 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
@@ -1,7 +1,6 @@
package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.bcpg.ArmoredOutputStream;
-import org.spongycastle.bcpg.S2K;
import org.spongycastle.bcpg.SignatureSubpacketTags;
import org.spongycastle.bcpg.sig.KeyFlags;
import org.spongycastle.openpgp.PGPKeyFlags;
@@ -14,6 +13,7 @@ import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureList;
import org.spongycastle.openpgp.PGPUtil;
+import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog;
@@ -22,20 +22,18 @@ import org.sufficientlysecure.keychain.service.OperationResultParcel.LogType;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
-import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
-import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
-import java.util.Vector;
/** Wrapper around PGPKeyRing class, to be constructed from bytes.
*
@@ -49,7 +47,7 @@ import java.util.Vector;
* 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 CanonicalizedKeyRing
* @see org.sufficientlysecure.keychain.pgp.UncachedPublicKey
* @see org.sufficientlysecure.keychain.pgp.UncachedSecretKey
*
@@ -59,18 +57,10 @@ public class UncachedKeyRing {
final PGPKeyRing mRing;
final boolean mIsSecret;
- final boolean mIsCanonicalized;
UncachedKeyRing(PGPKeyRing ring) {
mRing = ring;
mIsSecret = ring instanceof PGPSecretKeyRing;
- mIsCanonicalized = false;
- }
-
- private UncachedKeyRing(PGPKeyRing ring, boolean canonicalized) {
- mRing = ring;
- mIsSecret = ring instanceof PGPSecretKeyRing;
- mIsCanonicalized = canonicalized;
}
public long getMasterKeyId() {
@@ -81,11 +71,15 @@ public class UncachedKeyRing {
return new UncachedPublicKey(mRing.getPublicKey());
}
+ public UncachedPublicKey getPublicKey(long keyId) {
+ return new UncachedPublicKey(mRing.getPublicKey(keyId));
+ }
+
public Iterator<UncachedPublicKey> getPublicKeys() {
final Iterator<PGPPublicKey> it = mRing.getPublicKeys();
return new Iterator<UncachedPublicKey>() {
public void remove() {
- it.remove();
+ throw new UnsupportedOperationException();
}
public UncachedPublicKey next() {
return new UncachedPublicKey(it.next());
@@ -101,10 +95,6 @@ public class UncachedKeyRing {
return mIsSecret;
}
- public boolean isCanonicalized() {
- return mIsCanonicalized;
- }
-
public byte[] getEncoded() throws IOException {
return mRing.getEncoded();
}
@@ -115,43 +105,86 @@ public class UncachedKeyRing {
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 {
+
+ Iterator<UncachedKeyRing> parsed = fromStream(new ByteArrayInputStream(data));
+
+ if ( ! parsed.hasNext()) {
throw new PgpGeneralException("Object not recognized as PGPKeyRing!");
}
+
+ UncachedKeyRing ring = parsed.next();
+
+ if (parsed.hasNext()) {
+ throw new PgpGeneralException("Expected single keyring in stream, found at least two");
+ }
+
+ return ring;
+
}
- public static List<UncachedKeyRing> fromStream(InputStream stream)
- throws PgpGeneralException, IOException {
+ public static Iterator<UncachedKeyRing> fromStream(final InputStream stream) throws IOException {
+
+ return new Iterator<UncachedKeyRing>() {
- PGPObjectFactory objectFactory = new PGPObjectFactory(PGPUtil.getDecoderStream(stream));
+ UncachedKeyRing mNext = null;
+ PGPObjectFactory mObjectFactory = null;
- List<UncachedKeyRing> result = new Vector<UncachedKeyRing>();
+ private void cacheNext() {
+ if (mNext != null) {
+ return;
+ }
- // go through all objects in this block
- Object obj;
- while ((obj = objectFactory.nextObject()) != null) {
- Log.d(Constants.TAG, "Found class: " + obj.getClass());
+ try {
+ while(stream.available() > 0) {
+ // if there are no objects left from the last factory, create a new one
+ if (mObjectFactory == null) {
+ mObjectFactory = new PGPObjectFactory(PGPUtil.getDecoderStream(stream));
+ }
+
+ // go through all objects in this block
+ Object obj;
+ while ((obj = mObjectFactory.nextObject()) != null) {
+ Log.d(Constants.TAG, "Found class: " + obj.getClass());
+ if (!(obj instanceof PGPKeyRing)) {
+ Log.i(Constants.TAG,
+ "Skipping object of bad type " + obj.getClass().getName() + " in stream");
+ // skip object
+ continue;
+ }
+ mNext = new UncachedKeyRing((PGPKeyRing) obj);
+ return;
+ }
+ // if we are past the while loop, that means the objectFactory had no next
+ mObjectFactory = null;
+ }
+ } catch (IOException e) {
+ Log.e(Constants.TAG, "IOException while processing stream", e);
+ }
- if (obj instanceof PGPKeyRing) {
- result.add(new UncachedKeyRing((PGPKeyRing) obj));
- } else {
- Log.e(Constants.TAG, "Object not recognized as PGPKeyRing!");
}
- }
- return result;
+
+ @Override
+ public boolean hasNext() {
+ cacheNext();
+ return mNext != null;
+ }
+
+ @Override
+ public UncachedKeyRing next() {
+ try {
+ cacheNext();
+ return mNext;
+ } finally {
+ mNext = null;
+ }
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+
}
public void encodeArmored(OutputStream out, String version) throws IOException {
@@ -161,25 +194,6 @@ public class UncachedKeyRing {
aos.close();
}
- public HashSet<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!");
- }
-
- HashSet<Long> result = new HashSet<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;
- }
-
/** "Canonicalizes" a public key, removing inconsistencies in the process. This variant can be
* applied to public keyrings only.
*
@@ -189,7 +203,7 @@ public class UncachedKeyRing {
* - Remove all certificates flagged as "local"
* - Remove all certificates which are superseded by a newer one on the same target,
* including revocations with later re-certifications.
- * - Remove all certificates of unknown type:
+ * - Remove all certificates in other positions if not of known type:
* - key revocation signatures on the master key
* - subkey binding signatures for subkeys
* - certifications and certification revocations for user ids
@@ -204,7 +218,7 @@ public class UncachedKeyRing {
*
*/
@SuppressWarnings("ConstantConditions")
- public UncachedKeyRing canonicalize(OperationLog log, int indent) {
+ public CanonicalizedKeyRing canonicalize(OperationLog log, int indent) {
log.add(LogLevel.START, isSecret() ? LogType.MSG_KC_SECRET : LogType.MSG_KC_PUBLIC,
indent, PgpKeyHelper.convertKeyIdToHex(getMasterKeyId()));
@@ -225,7 +239,7 @@ public class UncachedKeyRing {
PGPPublicKey modified = masterKey;
PGPSignature revocation = null;
- for (PGPSignature zert : new IterableIterator<PGPSignature>(masterKey.getSignatures())) {
+ for (PGPSignature zert : new IterableIterator<PGPSignature>(masterKey.getKeySignatures())) {
int type = zert.getSignatureType();
// Disregard certifications on user ids, we will deal with those later
@@ -234,6 +248,10 @@ public class UncachedKeyRing {
|| type == PGPSignature.CASUAL_CERTIFICATION
|| type == PGPSignature.POSITIVE_CERTIFICATION
|| type == PGPSignature.CERTIFICATION_REVOCATION) {
+ // These should not be here...
+ log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD_TYPE_UID, indent);
+ modified = PGPPublicKey.removeCertification(modified, zert);
+ badCerts += 1;
continue;
}
WrappedSignature cert = new WrappedSignature(zert);
@@ -317,7 +335,7 @@ public class UncachedKeyRing {
if (cert.getCreationTime().after(now)) {
// Creation date in the future? No way!
- log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD_TIME, indent);
+ log.add(LogLevel.WARN, LogType.MSG_KC_UID_BAD_TIME, indent);
modified = PGPPublicKey.removeCertification(modified, zert);
badCerts += 1;
continue;
@@ -325,7 +343,7 @@ public class UncachedKeyRing {
if (cert.isLocal()) {
// Creation date in the future? No way!
- log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD_LOCAL, indent);
+ log.add(LogLevel.WARN, LogType.MSG_KC_UID_BAD_LOCAL, indent);
modified = PGPPublicKey.removeCertification(modified, zert);
badCerts += 1;
continue;
@@ -425,7 +443,7 @@ public class UncachedKeyRing {
// If no valid certificate (if only a revocation) remains, drop it
if (selfCert == null && revocation == null) {
modified = PGPPublicKey.removeCertification(modified, userId);
- log.add(LogLevel.ERROR, LogType.MSG_KC_UID_REVOKE_DUP,
+ log.add(LogLevel.ERROR, LogType.MSG_KC_UID_REMOVE,
indent, userId);
}
}
@@ -505,14 +523,17 @@ public class UncachedKeyRing {
continue;
}
- if (zert.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.KEY_FLAGS)) {
+ // if this certificate says it allows signing for the key
+ if (zert.getHashedSubPackets() != null &&
+ zert.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.KEY_FLAGS)) {
+
int flags = ((KeyFlags) zert.getHashedSubPackets()
.getSubpacket(SignatureSubpacketTags.KEY_FLAGS)).getFlags();
- // If this subkey is allowed to sign data,
if ((flags & PGPKeyFlags.CAN_SIGN) == PGPKeyFlags.CAN_SIGN) {
+ boolean ok = false;
+ // it MUST have an embedded primary key binding signature
try {
PGPSignatureList list = zert.getUnhashedSubPackets().getEmbeddedSignatures();
- boolean ok = false;
for (int i = 0; i < list.size(); i++) {
WrappedSignature subsig = new WrappedSignature(list.get(i));
if (subsig.getSignatureType() == PGPSignature.PRIMARYKEY_BINDING) {
@@ -526,17 +547,19 @@ public class UncachedKeyRing {
}
}
}
- if (!ok) {
- log.add(LogLevel.WARN, LogType.MSG_KC_SUB_PRIMARY_NONE, indent);
- badCerts += 1;
- continue;
- }
} catch (Exception e) {
log.add(LogLevel.WARN, LogType.MSG_KC_SUB_PRIMARY_BAD_ERR, indent);
badCerts += 1;
continue;
}
+ // if it doesn't, get rid of this!
+ if (!ok) {
+ log.add(LogLevel.WARN, LogType.MSG_KC_SUB_PRIMARY_NONE, indent);
+ badCerts += 1;
+ continue;
+ }
}
+
}
// if we already have a cert, and this one is not newer: skip it
@@ -549,6 +572,8 @@ public class UncachedKeyRing {
selfCert = zert;
// if this is newer than a possibly existing revocation, drop that one
if (revocation != null && selfCert.getCreationTime().after(revocation.getCreationTime())) {
+ log.add(LogLevel.DEBUG, LogType.MSG_KC_SUB_REVOKE_DUP, indent);
+ redundantCerts += 1;
revocation = null;
}
@@ -558,7 +583,7 @@ public class UncachedKeyRing {
// make sure the certificate checks out
try {
cert.init(masterKey);
- if (!cert.verifySignature(key)) {
+ if (!cert.verifySignature(masterKey, key)) {
log.add(LogLevel.WARN, LogType.MSG_KC_SUB_REVOKE_BAD, indent);
badCerts += 1;
continue;
@@ -582,7 +607,7 @@ public class UncachedKeyRing {
// it is not properly bound? error!
if (selfCert == null) {
- ring = replacePublicKey(ring, modified);
+ ring = removeSubKey(ring, key);
log.add(LogLevel.ERROR, LogType.MSG_KC_SUB_NO_CERT,
indent, PgpKeyHelper.convertKeyIdToHex(key.getKeyID()));
@@ -615,7 +640,8 @@ public class UncachedKeyRing {
log.add(LogLevel.OK, LogType.MSG_KC_SUCCESS, indent);
}
- return new UncachedKeyRing(ring, true);
+ return isSecret() ? new CanonicalizedSecretKeyRing((PGPSecretKeyRing) ring, 1)
+ : new CanonicalizedPublicKeyRing((PGPPublicKeyRing) ring, 0);
}
/** This operation merges information from a different keyring, returning a combined
@@ -650,7 +676,7 @@ public class UncachedKeyRing {
return left.length - right.length;
}
// compare byte-by-byte
- for (int i = 0; i < left.length && i < right.length; i++) {
+ for (int i = 0; i < left.length; i++) {
if (left[i] != right[i]) {
return (left[i] & 0xff) - (right[i] & 0xff);
}
@@ -678,7 +704,14 @@ public class UncachedKeyRing {
final PGPPublicKey resultKey = result.getPublicKey(key.getKeyID());
if (resultKey == null) {
log.add(LogLevel.DEBUG, LogType.MSG_MG_NEW_SUBKEY, indent);
- result = replacePublicKey(result, key);
+ // special case: if both rings are secret, copy over the secret key
+ if (isSecret() && other.isSecret()) {
+ PGPSecretKey sKey = ((PGPSecretKeyRing) candidate).getSecretKey(key.getKeyID());
+ result = PGPSecretKeyRing.insertSecretKey((PGPSecretKeyRing) result, sKey);
+ } else {
+ // otherwise, just insert the public key
+ result = replacePublicKey(result, key);
+ }
continue;
}
@@ -686,17 +719,7 @@ public class UncachedKeyRing {
PGPPublicKey modified = resultKey;
// Iterate certifications
- for (PGPSignature cert : new IterableIterator<PGPSignature>(key.getSignatures())) {
- int type = cert.getSignatureType();
- // Disregard certifications on user ids, we will deal with those later
- if (type == PGPSignature.NO_CERTIFICATION
- || type == PGPSignature.DEFAULT_CERTIFICATION
- || type == PGPSignature.CASUAL_CERTIFICATION
- || type == PGPSignature.POSITIVE_CERTIFICATION
- || type == PGPSignature.CERTIFICATION_REVOCATION) {
- continue;
- }
-
+ for (PGPSignature cert : new IterableIterator<PGPSignature>(key.getKeySignatures())) {
// Don't merge foreign stuff into secret keys
if (cert.getKeyID() != masterKeyId && isSecret()) {
continue;
@@ -744,8 +767,12 @@ public class UncachedKeyRing {
}
- log.add(LogLevel.DEBUG, LogType.MSG_MG_FOUND_NEW,
- indent, Integer.toString(newCerts));
+ if (newCerts > 0) {
+ log.add(LogLevel.DEBUG, LogType.MSG_MG_FOUND_NEW, indent,
+ Integer.toString(newCerts));
+ } else {
+ log.add(LogLevel.DEBUG, LogType.MSG_MG_UNCHANGED, indent);
+ }
return new UncachedKeyRing(result);
@@ -756,19 +783,20 @@ public class UncachedKeyRing {
}
- public UncachedKeyRing extractPublicKeyRing() {
+ public UncachedKeyRing extractPublicKeyRing() throws IOException {
if(!isSecret()) {
throw new RuntimeException("Tried to extract public keyring from non-secret keyring. " +
"This is a programming error and should never happen!");
}
- ArrayList<PGPPublicKey> keys = new ArrayList();
Iterator<PGPPublicKey> it = mRing.getPublicKeys();
+ ByteArrayOutputStream stream = new ByteArrayOutputStream(2048);
while (it.hasNext()) {
- keys.add(it.next());
+ stream.write(it.next().getEncoded());
}
- return new UncachedKeyRing(new PGPPublicKeyRing(keys));
+ return new UncachedKeyRing(
+ new PGPPublicKeyRing(stream.toByteArray(), new JcaKeyFingerprintCalculator()));
}
/** This method replaces a public key in a keyring.
@@ -792,4 +820,20 @@ public class UncachedKeyRing {
return PGPSecretKeyRing.insertSecretKey(secRing, sKey);
}
+ /** This method removes a subkey in a keyring.
+ *
+ * This method essentially wraps PGP*KeyRing.remove*Key, where the keyring may be of either
+ * the secret or public subclass.
+ *
+ * @return the resulting PGPKeyRing of the same type as the input
+ */
+ private static PGPKeyRing removeSubKey(PGPKeyRing ring, PGPPublicKey key) {
+ if (ring instanceof PGPPublicKeyRing) {
+ return PGPPublicKeyRing.removePublicKey((PGPPublicKeyRing) ring, key);
+ } else {
+ PGPSecretKey sKey = ((PGPSecretKeyRing) ring).getSecretKey(key.getKeyID());
+ return PGPSecretKeyRing.removeSecretKey((PGPSecretKeyRing) ring, sKey);
+ }
+ }
+
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java
index 33db7771b..358b1c552 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java
@@ -1,16 +1,14 @@
package org.sufficientlysecure.keychain.pgp;
-import org.spongycastle.bcpg.SignatureSubpacketTags;
import org.spongycastle.bcpg.sig.KeyFlags;
-import org.spongycastle.openpgp.PGPException;
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 org.sufficientlysecure.keychain.util.Log;
-import java.security.SignatureException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
@@ -44,14 +42,19 @@ public class UncachedPublicKey {
}
public Date getExpiryTime() {
- Date creationDate = getCreationTime();
- if (mPublicKey.getValidDays() == 0) {
+ long seconds = mPublicKey.getValidSeconds();
+ if (seconds > Integer.MAX_VALUE) {
+ Log.e(Constants.TAG, "error, expiry time too large");
+ return null;
+ }
+ if (seconds == 0) {
// no expiry
return null;
}
+ Date creationDate = getCreationTime();
Calendar calendar = GregorianCalendar.getInstance();
calendar.setTime(creationDate);
- calendar.add(Calendar.DATE, mPublicKey.getValidDays());
+ calendar.add(Calendar.SECOND, (int) seconds);
return calendar.getTime();
}
@@ -77,26 +80,76 @@ public class UncachedPublicKey {
return mPublicKey.getBitStrength();
}
+ /** Returns the primary user id, as indicated by the public key's self certificates.
+ *
+ * This is an expensive operation, since potentially a lot of certificates (and revocations)
+ * have to be checked, and even then the result is NOT guaranteed to be constant through a
+ * canonicalization operation.
+ *
+ * Returns null if there is no primary user id (as indicated by certificates)
+ *
+ */
public String getPrimaryUserId() {
+ String found = null;
+ PGPSignature foundSig = null;
for (String userId : new IterableIterator<String>(mPublicKey.getUserIDs())) {
+ PGPSignature revocation = null;
+
for (PGPSignature sig : new IterableIterator<PGPSignature>(mPublicKey.getSignaturesForID(userId))) {
- if (sig.getHashedSubPackets() != null
- && sig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.PRIMARY_USER_ID)) {
- try {
+ try {
+
+ // if this is a revocation, this is not the user id
+ if (sig.getSignatureType() == PGPSignature.CERTIFICATION_REVOCATION) {
+ // make sure it's actually valid
+ sig.init(new JcaPGPContentVerifierBuilderProvider().setProvider(
+ Constants.BOUNCY_CASTLE_PROVIDER_NAME), mPublicKey);
+ if (!sig.verifyCertification(userId, mPublicKey)) {
+ continue;
+ }
+ if (found != null && found.equals(userId)) {
+ found = null;
+ }
+ revocation = sig;
+ // this revocation may still be overridden by a newer cert
+ continue;
+ }
+
+ if (sig.getHashedSubPackets() != null && sig.getHashedSubPackets().isPrimaryUserID()) {
+ if (foundSig != null && sig.getCreationTime().before(foundSig.getCreationTime())) {
+ continue;
+ }
+ // ignore if there is a newer revocation for this user id
+ if (revocation != null && sig.getCreationTime().before(revocation.getCreationTime())) {
+ continue;
+ }
// make sure it's actually valid
sig.init(new JcaPGPContentVerifierBuilderProvider().setProvider(
Constants.BOUNCY_CASTLE_PROVIDER_NAME), mPublicKey);
if (sig.verifyCertification(userId, mPublicKey)) {
- return userId;
+ found = userId;
+ foundSig = sig;
+ // this one can't be relevant anymore at this point
+ revocation = null;
}
- } catch (Exception e) {
- // nothing bad happens, the key is just not considered the primary key id
}
- }
+ } catch (Exception e) {
+ // nothing bad happens, the key is just not considered the primary key id
+ }
}
}
- return null;
+ return found;
+ }
+
+ /**
+ * Returns primary user id if existing. If not, return first encountered user id.
+ */
+ public String getPrimaryUserIdWithFallback() {
+ String userId = getPrimaryUserId();
+ if (userId == null) {
+ userId = (String) mPublicKey.getUserIDs().next();
+ }
+ return userId;
}
public ArrayList<String> getUnorderedUserIds() {
@@ -186,6 +239,21 @@ public class UncachedPublicKey {
return mPublicKey;
}
+ public Iterator<WrappedSignature> getSignatures() {
+ final Iterator<PGPSignature> it = mPublicKey.getSignatures();
+ return new Iterator<WrappedSignature>() {
+ public void remove() {
+ it.remove();
+ }
+ public WrappedSignature next() {
+ return new WrappedSignature(it.next());
+ }
+ public boolean hasNext() {
+ return it.hasNext();
+ }
+ };
+ }
+
public Iterator<WrappedSignature> getSignaturesForId(String userId) {
final Iterator<PGPSignature> it = mPublicKey.getSignaturesForID(userId);
return new Iterator<WrappedSignature>() {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java
index 196ac1dee..07fb4fb9e 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java
@@ -15,6 +15,7 @@ import org.sufficientlysecure.keychain.util.Log;
import java.io.IOException;
import java.security.SignatureException;
+import java.util.ArrayList;
import java.util.Date;
/** OpenKeychain wrapper around PGPSignature objects.
@@ -55,12 +56,41 @@ public class WrappedSignature {
return mSig.getCreationTime();
}
+ public ArrayList<WrappedSignature> getEmbeddedSignatures() {
+ ArrayList<WrappedSignature> sigs = new ArrayList<WrappedSignature>();
+ if (!mSig.hasSubpackets()) {
+ return sigs;
+ }
+ try {
+ PGPSignatureList list;
+ if (mSig.getHashedSubPackets() != null) {
+ list = mSig.getHashedSubPackets().getEmbeddedSignatures();
+ for (int i = 0; i < list.size(); i++) {
+ sigs.add(new WrappedSignature(list.get(i)));
+ }
+ }
+ if (mSig.getUnhashedSubPackets() != null) {
+ list = mSig.getUnhashedSubPackets().getEmbeddedSignatures();
+ for (int i = 0; i < list.size(); i++) {
+ sigs.add(new WrappedSignature(list.get(i)));
+ }
+ }
+ } catch (PGPException e) {
+ // no matter
+ Log.e(Constants.TAG, "exception reading embedded signatures", e);
+ } catch (IOException e) {
+ // no matter
+ Log.e(Constants.TAG, "exception reading embedded signatures", e);
+ }
+ return sigs;
+ }
+
public byte[] getEncoded() throws IOException {
return mSig.getEncoded();
}
public boolean isRevocation() {
- return mSig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.REVOCATION_REASON);
+ return mSig.getSignatureType() == PGPSignature.CERTIFICATION_REVOCATION;
}
public boolean isPrimaryUserId() {
@@ -71,6 +101,9 @@ public class WrappedSignature {
if(!isRevocation()) {
throw new PgpGeneralException("Not a revocation signature.");
}
+ if (mSig.getHashedSubPackets() == null) {
+ return null;
+ }
SignatureSubpacket p = mSig.getHashedSubPackets().getSubpacket(
SignatureSubpacketTags.REVOCATION_REASON);
// For some reason, this is missing in SignatureSubpacketInputStream:146
@@ -80,7 +113,7 @@ public class WrappedSignature {
return ((RevocationReason) p).getRevocationDescription();
}
- public void init(WrappedPublicKey key) throws PgpGeneralException {
+ public void init(CanonicalizedPublicKey key) throws PgpGeneralException {
init(key.getPublicKey());
}
@@ -158,7 +191,7 @@ public class WrappedSignature {
public boolean verifySignature(UncachedPublicKey key, String uid) throws PgpGeneralException {
return verifySignature(key.getPublicKey(), uid);
}
- public boolean verifySignature(WrappedPublicKey key, String uid) throws PgpGeneralException {
+ public boolean verifySignature(CanonicalizedPublicKey key, String uid) throws PgpGeneralException {
return verifySignature(key.getPublicKey(), uid);
}
@@ -179,7 +212,7 @@ public class WrappedSignature {
}
public boolean isLocal() {
- if (!mSig.hasSubpackets()
+ if (mSig.getHashedSubPackets() == null
|| !mSig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.EXPORTABLE)) {
return false;
}