diff options
Diffstat (limited to 'OpenKeychain/src/main/java/org')
49 files changed, 2688 insertions, 1684 deletions
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);  | 
