diff options
author | Vincent Breitmoser <valodim@mugenguild.com> | 2015-04-24 14:18:01 +0200 |
---|---|---|
committer | Vincent Breitmoser <valodim@mugenguild.com> | 2015-04-24 14:18:01 +0200 |
commit | b4aec3114d9911cf9aef0d14ee697e5131b2853f (patch) | |
tree | 7237de5955ec34d8849737b2f9229bfbb37d0c45 /OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service | |
parent | c2163460971cc01e65d7dfd18eec491f01ebc744 (diff) | |
parent | baac30508d24dcda6135bf8ae338c99d8c3b8ad8 (diff) | |
download | open-keychain-b4aec3114d9911cf9aef0d14ee697e5131b2853f.tar.gz open-keychain-b4aec3114d9911cf9aef0d14ee697e5131b2853f.tar.bz2 open-keychain-b4aec3114d9911cf9aef0d14ee697e5131b2853f.zip |
Merge branch 'development' into linked-identities
Conflicts:
Graphics/update-drawables.sh
OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/operations/CertifyOperationTest.java
OpenKeychain/build.gradle
OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java
OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java
OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java
Diffstat (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service')
7 files changed, 475 insertions, 88 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 57a11de21..a7571a7ac 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java @@ -22,10 +22,14 @@ import android.os.Parcel; import android.os.Parcelable; import java.io.Serializable; +import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; +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 e210536fd..63ea6285c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -46,7 +46,6 @@ import org.sufficientlysecure.keychain.operations.results.CertifyResult; import org.sufficientlysecure.keychain.operations.results.ConsolidateResult; import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult; import org.sufficientlysecure.keychain.operations.results.DeleteResult; -import org.sufficientlysecure.keychain.operations.results.EditKeyResult; import org.sufficientlysecure.keychain.operations.results.ExportResult; import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; import org.sufficientlysecure.keychain.operations.results.CertifyResult; @@ -68,6 +67,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; @@ -159,8 +159,6 @@ public class KeychainIntentService extends IntentService implements Progressable // decrypt/verify public static final String DECRYPT_CIPHERTEXT_BYTES = "ciphertext_bytes"; - public static final String DECRYPT_PASSPHRASE = "passphrase"; - public static final String DECRYPT_NFC_DECRYPTED_SESSION_KEY = "nfc_decrypted_session_key"; // keybase proof public static final String KEYBASE_REQUIRED_FINGERPRINT = "keybase_required_fingerprint"; @@ -169,6 +167,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"; @@ -193,7 +192,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"; @@ -260,11 +259,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); @@ -289,27 +289,20 @@ public class KeychainIntentService extends IntentService implements Progressable case ACTION_DECRYPT_METADATA: { try { - /* Input */ - Passphrase passphrase = data.getParcelable(DECRYPT_PASSPHRASE); - byte[] nfcDecryptedSessionKey = data.getByteArray(DECRYPT_NFC_DECRYPTED_SESSION_KEY); + /* Input */ + CryptoInputParcel cryptoInput = data.getParcelable(EXTRA_CRYPTO_INPUT); InputData inputData = createDecryptInputData(data); - /* Operation */ - - Bundle resultData = new Bundle(); - // verifyText and decrypt returning additional resultData values for the // verification of signatures PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder( this, new ProviderHelper(this), this, inputData, null ); builder.setAllowSymmetricDecryption(true) - .setPassphrase(passphrase) - .setDecryptMetadataOnly(true) - .setNfcState(nfcDecryptedSessionKey); + .setDecryptMetadataOnly(true); - DecryptVerifyResult decryptVerifyResult = builder.build().execute(); + DecryptVerifyResult decryptVerifyResult = builder.build().execute(cryptoInput); sendMessageToHandler(MessageStatus.OKAY, decryptVerifyResult); } catch (Exception e) { @@ -384,7 +377,8 @@ public class KeychainIntentService extends IntentService implements Progressable ); builder.setSignedLiteralData(true).setRequiredSignerFingerprint(requiredFingerprint); - DecryptVerifyResult decryptVerifyResult = builder.build().execute(); + DecryptVerifyResult decryptVerifyResult = builder.build().execute( + new CryptoInputParcel()); outStream.close(); if (!decryptVerifyResult.success()) { @@ -419,15 +413,13 @@ public class KeychainIntentService extends IntentService implements Progressable case ACTION_DECRYPT_VERIFY: { try { - /* Input */ - Passphrase passphrase = data.getParcelable(DECRYPT_PASSPHRASE); - byte[] nfcDecryptedSessionKey = data.getByteArray(DECRYPT_NFC_DECRYPTED_SESSION_KEY); + /* Input */ + CryptoInputParcel cryptoInput = data.getParcelable(EXTRA_CRYPTO_INPUT); InputData inputData = createDecryptInputData(data); OutputStream outStream = createCryptOutputStream(data); - /* Operation */ - + /* Operation */ Bundle resultData = new Bundle(); // verifyText and decrypt returning additional resultData values for the @@ -436,24 +428,22 @@ public class KeychainIntentService extends IntentService implements Progressable this, new ProviderHelper(this), this, inputData, outStream ); - builder.setAllowSymmetricDecryption(true) - .setPassphrase(passphrase) - .setNfcState(nfcDecryptedSessionKey); + builder.setAllowSymmetricDecryption(true); - DecryptVerifyResult decryptVerifyResult = builder.build().execute(); + DecryptVerifyResult decryptVerifyResult = builder.build().execute(cryptoInput); outStream.close(); resultData.putParcelable(DecryptVerifyResult.EXTRA_RESULT, decryptVerifyResult); - /* Output */ - + /* Output */ finalizeDecryptOutputStream(data, resultData, outStream); - Log.logDebugBundle(resultData, "resultData"); sendMessageToHandler(MessageStatus.OKAY, resultData); - } catch (Exception e) { + + } catch (IOException | PgpGeneralException e) { + // TODO get rid of this! sendErrorToHandler(e); } @@ -478,11 +468,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); @@ -492,11 +482,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); @@ -553,11 +544,12 @@ public class KeychainIntentService extends IntentService implements Progressable // Input SignEncryptParcel inputParcel = data.getParcelable(SIGN_ENCRYPT_PARCEL); + CryptoInputParcel cryptoInput = data.getParcelable(EXTRA_CRYPTO_INPUT); // Operation SignEncryptOperation op = new SignEncryptOperation( this, new ProviderHelper(this), this, mActionCanceled); - SignEncryptResult result = op.execute(inputParcel); + SignEncryptResult result = op.execute(inputParcel, cryptoInput); // 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..78137170d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java @@ -60,18 +60,18 @@ import java.util.Date; * * Caching behavior for subkeys depends on the cacheSubs preference: * - * - If cacheSubs is NOT set, passphrases will be cached and retrieved by master key id. The - * checks for special subkeys will still be done, but otherwise it is assumed that all subkeys - * from the same master key will use the same passphrase. This can lead to bad passphrase - * errors if two subkeys are encrypted differently. This is the default behavior. + * - If cacheSubs is NOT set, passphrases will be cached and retrieved by master key id. The + * checks for special subkeys will still be done, but otherwise it is assumed that all subkeys + * from the same master key will use the same passphrase. This can lead to bad passphrase + * errors if two subkeys are encrypted differently. This is the default behavior. * - * - If cacheSubs IS set, passphrases will be cached per subkey id. This means that if a keyring - * has two subkeys for different purposes, passphrases will be cached independently and the - * user will be asked for a passphrase once per subkey even if it is the same one. This mode - * of operation is more precise, since we can assume that all passphrases returned from cache - * will be correct without fail. Since keyrings with differently encrypted subkeys are a very - * rare occurrence, and caching by keyring is what the user expects in the vast majority of - * cases, this is not the default behavior. + * - If cacheSubs IS set, passphrases will be cached per subkey id. This means that if a keyring + * has two subkeys for different purposes, passphrases will be cached independently and the + * user will be asked for a passphrase once per subkey even if it is the same one. This mode + * of operation is more precise, since we can assume that all passphrases returned from cache + * will be correct without fail. Since keyrings with differently encrypted subkeys are a very + * rare occurrence, and caching by keyring is what the user expects in the vast majority of + * cases, this is not the default behavior. * */ public class PassphraseCacheService extends Service { @@ -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,10 +137,23 @@ 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. - + * * @return passphrase or null (if no passphrase is cached for this keyId) */ public static Passphrase getCachedPassphrase(Context context, long masterKeyId, long subKeyId) throws KeyNotFoundException { @@ -218,7 +231,7 @@ public class PassphraseCacheService extends Service { } // on "none" key, just do nothing - if(masterKeyId == Constants.key.none) { + if (masterKeyId == Constants.key.none) { return null; } @@ -232,11 +245,11 @@ public class PassphraseCacheService extends Service { switch (keyType) { case DIVERT_TO_CARD: - if (Preferences.getPreferences(this).useDefaultYubikeyPin()) { - Log.d(Constants.TAG, "PassphraseCacheService: Using default Yubikey PIN: 123456"); - return new Passphrase("123456"); // default Yubikey PIN, see http://www.yubico.com/2012/12/yubikey-neo-openpgp/ + if (Preferences.getPreferences(this).useDefaultYubiKeyPin()) { + Log.d(Constants.TAG, "PassphraseCacheService: Using default YubiKey PIN: 123456"); + return new Passphrase("123456"); // default YubiKey PIN, see http://www.yubico.com/2012/12/yubikey-neo-openpgp/ } else { - Log.d(Constants.TAG, "PassphraseCacheService: NOT using default Yubikey PIN"); + Log.d(Constants.TAG, "PassphraseCacheService: NOT using default YubiKey PIN"); break; } case PASSPHRASE_EMPTY: @@ -310,11 +323,11 @@ public class PassphraseCacheService extends Service { /** * Build pending intent that is executed by alarm manager to time out a specific passphrase */ - private static PendingIntent buildIntent(Context context, long keyId) { + private static PendingIntent buildIntent(Context context, long referenceKeyId) { Intent intent = new Intent(BROADCAST_ACTION_PASSPHRASE_CACHE_SERVICE); - intent.putExtra(EXTRA_KEY_ID, keyId); + intent.putExtra(EXTRA_KEY_ID, referenceKeyId); // request code should be unique for each PendingIntent, thus keyId is used - return PendingIntent.getBroadcast(context, (int) keyId, intent, + return PendingIntent.getBroadcast(context, (int) referenceKeyId, intent, PendingIntent.FLAG_CANCEL_CURRENT); } @@ -325,11 +338,17 @@ public class PassphraseCacheService extends Service { public int onStartCommand(Intent intent, int flags, int startId) { Log.d(Constants.TAG, "PassphraseCacheService.onStartCommand()"); + if (intent == null || intent.getAction() == null) { + updateService(); + return START_STICKY; + } + // register broadcastreceiver registerReceiver(); - if (intent != null && intent.getAction() != null) { - if (ACTION_PASSPHRASE_CACHE_ADD.equals(intent.getAction())) { + String action = intent.getAction(); + switch (action) { + case ACTION_PASSPHRASE_CACHE_ADD: { long ttl = intent.getLongExtra(EXTRA_TTL, DEFAULT_TTL); long masterKeyId = intent.getLongExtra(EXTRA_KEY_ID, -1); long subKeyId = intent.getLongExtra(EXTRA_SUBKEY_ID, -1); @@ -343,28 +362,19 @@ public class PassphraseCacheService extends Service { ); // if we don't cache by specific subkey id, or the requested subkey is the master key, - // just add master key id to the cache - if (subKeyId == masterKeyId || !Preferences.getPreferences(mContext).getPassphraseCacheSubs()) { - mPassphraseCache.put(masterKeyId, new CachedPassphrase(passphrase, primaryUserID)); - if (ttl > 0) { - // register new alarm with keyId for this passphrase - long triggerTime = new Date().getTime() + (ttl * 1000); - AlarmManager am = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE); - am.set(AlarmManager.RTC_WAKEUP, triggerTime, buildIntent(this, masterKeyId)); - } - } else { - // otherwise, add this specific subkey to the cache - mPassphraseCache.put(subKeyId, new CachedPassphrase(passphrase, primaryUserID)); - if (ttl > 0) { - // register new alarm with keyId for this passphrase - long triggerTime = new Date().getTime() + (ttl * 1000); - AlarmManager am = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE); - am.set(AlarmManager.RTC_WAKEUP, triggerTime, buildIntent(this, subKeyId)); - } + // just add master key id to the cache, otherwise, add this specific subkey to the cache + long referenceKeyId = + Preferences.getPreferences(mContext).getPassphraseCacheSubs() ? subKeyId : masterKeyId; + mPassphraseCache.put(referenceKeyId, new CachedPassphrase(passphrase, primaryUserID)); + if (ttl > 0) { + // register new alarm with keyId for this passphrase + long triggerTime = new Date().getTime() + (ttl * 1000); + AlarmManager am = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE); + am.set(AlarmManager.RTC_WAKEUP, triggerTime, buildIntent(this, referenceKeyId)); } - - updateService(); - } else if (ACTION_PASSPHRASE_CACHE_GET.equals(intent.getAction())) { + break; + } + case ACTION_PASSPHRASE_CACHE_GET: { long masterKeyId = intent.getLongExtra(EXTRA_KEY_ID, Constants.key.symmetric); long subKeyId = intent.getLongExtra(EXTRA_SUBKEY_ID, Constants.key.symmetric); Messenger messenger = intent.getParcelableExtra(EXTRA_MESSENGER); @@ -392,22 +402,42 @@ public class PassphraseCacheService extends Service { } catch (RemoteException e) { Log.e(Constants.TAG, "PassphraseCacheService: Sending message failed", e); } - } else if (ACTION_PASSPHRASE_CACHE_CLEAR.equals(intent.getAction())) { + break; + } + case ACTION_PASSPHRASE_CACHE_CLEAR: { 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 referenceKeyId; + if (Preferences.getPreferences(mContext).getPassphraseCacheSubs()) { + referenceKeyId = intent.getLongExtra(EXTRA_KEY_ID, 0L); + } else { + referenceKeyId = intent.getLongExtra(EXTRA_SUBKEY_ID, 0L); + } + // Stop specific ttl alarm and + am.cancel(buildIntent(this, referenceKeyId)); + mPassphraseCache.delete(referenceKeyId); - updateService(); - } else { + } else { + + // Stop all ttl alarms + for (int i = 0; i < mPassphraseCache.size(); i++) { + am.cancel(buildIntent(this, mPassphraseCache.keyAt(i))); + } + mPassphraseCache.clear(); + + } + break; + } + default: { Log.e(Constants.TAG, "PassphraseCacheService: Intent or Intent Action not supported!"); + break; } } + updateService(); + return START_STICKY; } 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..3d1ccaca1 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/CryptoInputParcel.java @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de> + * + * 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.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..535c1e735 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/RequiredInputParcel.java @@ -0,0 +1,214 @@ +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; + +import org.sufficientlysecure.keychain.Constants.key; + + +public class RequiredInputParcel implements Parcelable { + + public enum RequiredInputType { + PASSPHRASE, PASSPHRASE_SYMMETRIC, 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()]; + + // 0 = none, 1 = both, 2 = only hashes (decrypt) + int hashTypes = source.readInt(); + if (hashTypes != 0) { + int count = source.readInt(); + mInputHashes = new byte[count][]; + if (hashTypes == 1) { + mSignAlgos = new int[count]; + for (int i = 0; i < count; i++) { + mInputHashes[i] = source.createByteArray(); + mSignAlgos[i] = source.readInt(); + } + } else { + mSignAlgos = null; + for (int i = 0; i < count; i++) { + mInputHashes[i] = source.createByteArray(); + } + } + } 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, long subKeyId) { + return new RequiredInputParcel(RequiredInputType.NFC_DECRYPT, + new byte[][] { inputHash }, null, null, null, subKeyId); + } + + public static RequiredInputParcel createRequiredSignPassphrase( + long masterKeyId, long subKeyId, Date signatureTime) { + return new RequiredInputParcel(RequiredInputType.PASSPHRASE, + null, null, signatureTime, masterKeyId, subKeyId); + } + + public static RequiredInputParcel createRequiredDecryptPassphrase( + long masterKeyId, long subKeyId) { + return new RequiredInputParcel(RequiredInputType.PASSPHRASE, + null, null, null, masterKeyId, subKeyId); + } + + public static RequiredInputParcel createRequiredSymmetricPassphrase() { + return new RequiredInputParcel(RequiredInputType.PASSPHRASE_SYMMETRIC, + null, null, null, null, null); + } + + 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(mSignAlgos != null ? 1 : 2); + dest.writeInt(mInputHashes.length); + for (int i = 0; i < mInputHashes.length; i++) { + dest.writeByteArray(mInputHashes[i]); + if (mSignAlgos != null) { + 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(); + } + + } + +} |