diff options
Diffstat (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service')
7 files changed, 371 insertions, 13 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java index f4b941109..8721f4c0c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java @@ -22,9 +22,13 @@ import android.os.Parcel; import android.os.Parcelable; import java.io.Serializable; +import java.nio.ByteBuffer; import java.util.ArrayList; +import java.util.Date; +import java.util.Map; import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; +import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; /** 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 5ecfb29f5..1a94d70b7 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -60,6 +60,7 @@ import org.sufficientlysecure.keychain.pgp.SignEncryptParcel; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException; import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.service.ServiceProgressHandler.MessageStatus; import org.sufficientlysecure.keychain.util.FileHelper; import org.sufficientlysecure.keychain.util.InputData; @@ -161,6 +162,7 @@ public class KeychainIntentService extends IntentService implements Progressable // save keyring public static final String EDIT_KEYRING_PARCEL = "save_parcel"; public static final String EDIT_KEYRING_PASSPHRASE = "passphrase"; + public static final String EXTRA_CRYPTO_INPUT = "crypto_input"; // delete keyring(s) public static final String DELETE_KEY_LIST = "delete_list"; @@ -185,7 +187,7 @@ public class KeychainIntentService extends IntentService implements Progressable // promote key public static final String PROMOTE_MASTER_KEY_ID = "promote_master_key_id"; - public static final String PROMOTE_TYPE = "promote_type"; + public static final String PROMOTE_CARD_AID = "promote_card_aid"; // consolidate public static final String CONSOLIDATE_RECOVERY = "consolidate_recovery"; @@ -252,11 +254,12 @@ public class KeychainIntentService extends IntentService implements Progressable // Input CertifyActionsParcel parcel = data.getParcelable(CERTIFY_PARCEL); + CryptoInputParcel cryptoInput = data.getParcelable(EXTRA_CRYPTO_INPUT); String keyServerUri = data.getString(UPLOAD_KEY_SERVER); // Operation CertifyOperation op = new CertifyOperation(this, providerHelper, this, mActionCanceled); - CertifyResult result = op.certify(parcel, keyServerUri); + CertifyResult result = op.certify(parcel, cryptoInput, keyServerUri); // Result sendMessageToHandler(MessageStatus.OKAY, result); @@ -470,11 +473,11 @@ public class KeychainIntentService extends IntentService implements Progressable // Input SaveKeyringParcel saveParcel = data.getParcelable(EDIT_KEYRING_PARCEL); - Passphrase passphrase = data.getParcelable(EDIT_KEYRING_PASSPHRASE); + CryptoInputParcel cryptoInput = data.getParcelable(EXTRA_CRYPTO_INPUT); // Operation EditKeyOperation op = new EditKeyOperation(this, providerHelper, this, mActionCanceled); - EditKeyResult result = op.execute(saveParcel, passphrase); + OperationResult result = op.execute(saveParcel, cryptoInput); // Result sendMessageToHandler(MessageStatus.OKAY, result); @@ -484,11 +487,12 @@ public class KeychainIntentService extends IntentService implements Progressable case ACTION_PROMOTE_KEYRING: { // Input - long keyRingId = data.getInt(EXPORT_KEY_RING_MASTER_KEY_ID); + long keyRingId = data.getLong(PROMOTE_MASTER_KEY_ID); + byte[] cardAid = data.getByteArray(PROMOTE_CARD_AID); // Operation PromoteKeyOperation op = new PromoteKeyOperation(this, providerHelper, this, mActionCanceled); - PromoteKeyResult result = op.execute(keyRingId); + PromoteKeyResult result = op.execute(keyRingId, cardAid); // Result sendMessageToHandler(MessageStatus.OKAY, result); 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 93a2bee23..8e37a8867 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java @@ -123,7 +123,7 @@ public class PassphraseCacheService extends Service { public static void addCachedPassphrase(Context context, long masterKeyId, long subKeyId, Passphrase passphrase, String primaryUserId) { - Log.d(Constants.TAG, "PassphraseCacheService.cacheNewPassphrase() for " + masterKeyId); + Log.d(Constants.TAG, "PassphraseCacheService.addCachedPassphrase() for " + masterKeyId); Intent intent = new Intent(context, PassphraseCacheService.class); intent.setAction(ACTION_PASSPHRASE_CACHE_ADD); @@ -137,6 +137,19 @@ public class PassphraseCacheService extends Service { context.startService(intent); } + public static void clearCachedPassphrase(Context context, long masterKeyId, long subKeyId) { + Log.d(Constants.TAG, "PassphraseCacheService.clearCachedPassphrase() for " + masterKeyId); + + Intent intent = new Intent(context, PassphraseCacheService.class); + intent.setAction(ACTION_PASSPHRASE_CACHE_CLEAR); + + intent.putExtra(EXTRA_KEY_ID, masterKeyId); + intent.putExtra(EXTRA_SUBKEY_ID, subKeyId); + + context.startService(intent); + } + + /** * Gets a cached passphrase from memory by sending an intent to the service. This method is * designed to wait until the service returns the passphrase. @@ -395,12 +408,27 @@ public class PassphraseCacheService extends Service { } else if (ACTION_PASSPHRASE_CACHE_CLEAR.equals(intent.getAction())) { AlarmManager am = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE); - // Stop all ttl alarms - for (int i = 0; i < mPassphraseCache.size(); i++) { - am.cancel(buildIntent(this, mPassphraseCache.keyAt(i))); - } + if (intent.hasExtra(EXTRA_SUBKEY_ID) && intent.hasExtra(EXTRA_KEY_ID)) { - mPassphraseCache.clear(); + long keyId; + if (Preferences.getPreferences(mContext).getPassphraseCacheSubs()) { + keyId = intent.getLongExtra(EXTRA_KEY_ID, 0L); + } else { + keyId = intent.getLongExtra(EXTRA_SUBKEY_ID, 0L); + } + // Stop specific ttl alarm and + am.cancel(buildIntent(this, keyId)); + mPassphraseCache.delete(keyId); + + } else { + + // Stop all ttl alarms + for (int i = 0; i < mPassphraseCache.size(); i++) { + am.cancel(buildIntent(this, mPassphraseCache.keyAt(i))); + } + mPassphraseCache.clear(); + + } updateService(); } else { 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 9fd278c13..2e0524141 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java @@ -82,10 +82,14 @@ public class SaveKeyringParcel implements Parcelable { mRevokeSubKeys = new ArrayList<>(); } + public boolean isEmpty() { + return isRestrictedOnly() && mChangeSubKeys.isEmpty(); + } + /** Returns true iff this parcel does not contain any operations which require a passphrase. */ public boolean isRestrictedOnly() { if (mNewUnlock != null || !mAddUserIds.isEmpty() || !mAddUserAttribute.isEmpty() - || !mAddSubKeys.isEmpty() || mChangePrimaryUserId != null || !mRevokeSubKeys .isEmpty() + || !mAddSubKeys.isEmpty() || mChangePrimaryUserId != null || !mRevokeUserIds.isEmpty() || !mRevokeSubKeys.isEmpty()) { return false; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ServiceProgressHandler.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ServiceProgressHandler.java index 4bd3481e6..430d8a49b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ServiceProgressHandler.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ServiceProgressHandler.java @@ -18,6 +18,7 @@ package org.sufficientlysecure.keychain.service; import android.app.Activity; +import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.os.Message; @@ -26,6 +27,7 @@ import android.support.v4.app.FragmentManager; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.operations.results.CertifyResult; import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment; import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.util.Log; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/CryptoInputParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/CryptoInputParcel.java new file mode 100644 index 000000000..21aacd1f0 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/CryptoInputParcel.java @@ -0,0 +1,125 @@ +package org.sufficientlysecure.keychain.service.input; + +import java.nio.ByteBuffer; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import android.os.Parcel; +import android.os.Parcelable; + +import org.sufficientlysecure.keychain.util.Passphrase; + + +/** This is a base class for the input of crypto operations. + * + */ +public class CryptoInputParcel implements Parcelable { + + final Date mSignatureTime; + final Passphrase mPassphrase; + + // this map contains both decrypted session keys and signed hashes to be + // used in the crypto operation described by this parcel. + private HashMap<ByteBuffer,byte[]> mCryptoData = new HashMap<>(); + + public CryptoInputParcel() { + mSignatureTime = new Date(); + mPassphrase = null; + } + + public CryptoInputParcel(Date signatureTime, Passphrase passphrase) { + mSignatureTime = signatureTime == null ? new Date() : signatureTime; + mPassphrase = passphrase; + } + + public CryptoInputParcel(Passphrase passphrase) { + mSignatureTime = new Date(); + mPassphrase = passphrase; + } + + public CryptoInputParcel(Date signatureTime) { + mSignatureTime = signatureTime == null ? new Date() : signatureTime; + mPassphrase = null; + } + + protected CryptoInputParcel(Parcel source) { + mSignatureTime = new Date(source.readLong()); + mPassphrase = source.readParcelable(getClass().getClassLoader()); + + { + int count = source.readInt(); + mCryptoData = new HashMap<>(count); + for (int i = 0; i < count; i++) { + byte[] key = source.createByteArray(); + byte[] value = source.createByteArray(); + mCryptoData.put(ByteBuffer.wrap(key), value); + } + } + + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeLong(mSignatureTime.getTime()); + dest.writeParcelable(mPassphrase, 0); + + dest.writeInt(mCryptoData.size()); + for (HashMap.Entry<ByteBuffer,byte[]> entry : mCryptoData.entrySet()) { + dest.writeByteArray(entry.getKey().array()); + dest.writeByteArray(entry.getValue()); + } + } + + public void addCryptoData(byte[] hash, byte[] signedHash) { + mCryptoData.put(ByteBuffer.wrap(hash), signedHash); + } + + public Map<ByteBuffer, byte[]> getCryptoData() { + return Collections.unmodifiableMap(mCryptoData); + } + + public Date getSignatureTime() { + return mSignatureTime; + } + + public boolean hasPassphrase() { + return mPassphrase != null; + } + + public Passphrase getPassphrase() { + return mPassphrase; + } + + public static final Creator<CryptoInputParcel> CREATOR = new Creator<CryptoInputParcel>() { + public CryptoInputParcel createFromParcel(final Parcel source) { + return new CryptoInputParcel(source); + } + + public CryptoInputParcel[] newArray(final int size) { + return new CryptoInputParcel[size]; + } + }; + + @Override + public String toString() { + StringBuilder b = new StringBuilder(); + b.append("CryptoInput: { "); + b.append(mSignatureTime).append(" "); + if (mPassphrase != null) { + b.append("passphrase"); + } + if (mCryptoData != null) { + b.append(mCryptoData.size()); + b.append(" hashes "); + } + b.append("}"); + return b.toString(); + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/RequiredInputParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/RequiredInputParcel.java new file mode 100644 index 000000000..471fc0ec9 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/RequiredInputParcel.java @@ -0,0 +1,191 @@ +package org.sufficientlysecure.keychain.service.input; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; + +import android.os.Parcel; +import android.os.Parcelable; + + +public class RequiredInputParcel implements Parcelable { + + public enum RequiredInputType { + PASSPHRASE, NFC_SIGN, NFC_DECRYPT + } + + public Date mSignatureTime; + + public final RequiredInputType mType; + + public final byte[][] mInputHashes; + public final int[] mSignAlgos; + + private Long mMasterKeyId; + private Long mSubKeyId; + + private RequiredInputParcel(RequiredInputType type, byte[][] inputHashes, + int[] signAlgos, Date signatureTime, Long masterKeyId, Long subKeyId) { + mType = type; + mInputHashes = inputHashes; + mSignAlgos = signAlgos; + mSignatureTime = signatureTime; + mMasterKeyId = masterKeyId; + mSubKeyId = subKeyId; + } + + public RequiredInputParcel(Parcel source) { + mType = RequiredInputType.values()[source.readInt()]; + + if (source.readInt() != 0) { + int count = source.readInt(); + mInputHashes = new byte[count][]; + mSignAlgos = new int[count]; + for (int i = 0; i < count; i++) { + mInputHashes[i] = source.createByteArray(); + mSignAlgos[i] = source.readInt(); + } + } else { + mInputHashes = null; + mSignAlgos = null; + } + + mSignatureTime = source.readInt() != 0 ? new Date(source.readLong()) : null; + mMasterKeyId = source.readInt() != 0 ? source.readLong() : null; + mSubKeyId = source.readInt() != 0 ? source.readLong() : null; + + } + + public long getMasterKeyId() { + return mMasterKeyId; + } + + public long getSubKeyId() { + return mSubKeyId; + } + + public static RequiredInputParcel createNfcSignOperation( + byte[] inputHash, int signAlgo, Date signatureTime) { + return new RequiredInputParcel(RequiredInputType.NFC_SIGN, + new byte[][] { inputHash }, new int[] { signAlgo }, + signatureTime, null, null); + } + + public static RequiredInputParcel createNfcDecryptOperation(byte[] inputHash) { + return new RequiredInputParcel(RequiredInputType.NFC_DECRYPT, + new byte[][] { inputHash }, null, null, null, null); + } + + public static RequiredInputParcel createRequiredPassphrase( + long masterKeyId, long subKeyId, Date signatureTime) { + return new RequiredInputParcel(RequiredInputType.PASSPHRASE, + null, null, signatureTime, masterKeyId, subKeyId); + } + + public static RequiredInputParcel createRequiredPassphrase( + RequiredInputParcel req) { + return new RequiredInputParcel(RequiredInputType.PASSPHRASE, + null, null, req.mSignatureTime, req.mMasterKeyId, req.mSubKeyId); + } + + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mType.ordinal()); + if (mInputHashes != null) { + dest.writeInt(1); + dest.writeInt(mInputHashes.length); + for (int i = 0; i < mInputHashes.length; i++) { + dest.writeByteArray(mInputHashes[i]); + dest.writeInt(mSignAlgos[i]); + } + } else { + dest.writeInt(0); + } + if (mSignatureTime != null) { + dest.writeInt(1); + dest.writeLong(mSignatureTime.getTime()); + } else { + dest.writeInt(0); + } + if (mMasterKeyId != null) { + dest.writeInt(1); + dest.writeLong(mMasterKeyId); + } else { + dest.writeInt(0); + } + if (mSubKeyId != null) { + dest.writeInt(1); + dest.writeLong(mSubKeyId); + } else { + dest.writeInt(0); + } + + } + + public static final Creator<RequiredInputParcel> CREATOR = new Creator<RequiredInputParcel>() { + public RequiredInputParcel createFromParcel(final Parcel source) { + return new RequiredInputParcel(source); + } + + public RequiredInputParcel[] newArray(final int size) { + return new RequiredInputParcel[size]; + } + }; + + public static class NfcSignOperationsBuilder { + Date mSignatureTime; + ArrayList<Integer> mSignAlgos = new ArrayList<>(); + ArrayList<byte[]> mInputHashes = new ArrayList<>(); + long mMasterKeyId; + long mSubKeyId; + + public NfcSignOperationsBuilder(Date signatureTime, long masterKeyId, long subKeyId) { + mSignatureTime = signatureTime; + mMasterKeyId = masterKeyId; + mSubKeyId = subKeyId; + } + + public RequiredInputParcel build() { + byte[][] inputHashes = new byte[mInputHashes.size()][]; + mInputHashes.toArray(inputHashes); + int[] signAlgos = new int[mSignAlgos.size()]; + for (int i = 0; i < mSignAlgos.size(); i++) { + signAlgos[i] = mSignAlgos.get(i); + } + + return new RequiredInputParcel(RequiredInputType.NFC_SIGN, + inputHashes, signAlgos, mSignatureTime, mMasterKeyId, mSubKeyId); + } + + public void addHash(byte[] hash, int algo) { + mInputHashes.add(hash); + mSignAlgos.add(algo); + } + + public void addAll(RequiredInputParcel input) { + if (!mSignatureTime.equals(input.mSignatureTime)) { + throw new AssertionError("input times must match, this is a programming error!"); + } + if (input.mType != RequiredInputType.NFC_SIGN) { + throw new AssertionError("operation types must match, this is a progrmming error!"); + } + + Collections.addAll(mInputHashes, input.mInputHashes); + for (int signAlgo : input.mSignAlgos) { + mSignAlgos.add(signAlgo); + } + } + + public boolean isEmpty() { + return mInputHashes.isEmpty(); + } + + } + +} |