diff options
author | Dominik Schürmann <dominik@dominikschuermann.de> | 2014-06-03 13:06:49 +0200 |
---|---|---|
committer | Dominik Schürmann <dominik@dominikschuermann.de> | 2014-06-03 13:06:49 +0200 |
commit | 84d03dd5142902e013d52dca256d0a66d551ee6e (patch) | |
tree | 8560088fe57ab8454fa8c35e21bbff7591085fb5 /OpenKeychain/src/main/java/org | |
parent | 39997a802ec2a56039d216cd44c3ec89ab75c1a2 (diff) | |
parent | 4a47126f05ee53c9d09f46b9445918230937ccf2 (diff) | |
download | open-keychain-84d03dd5142902e013d52dca256d0a66d551ee6e.tar.gz open-keychain-84d03dd5142902e013d52dca256d0a66d551ee6e.tar.bz2 open-keychain-84d03dd5142902e013d52dca256d0a66d551ee6e.zip |
Merge pull request #643 from open-keychain/v/dev
wrap bouncycastle objects and don't use them in android-related code
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); |