Cache CryptoInputParcel in OpenPgpService

This commit is contained in:
Dominik Schürmann 2015-04-10 15:58:37 +02:00
parent 5ea01a15d3
commit 7074b44347
4 changed files with 69 additions and 24 deletions

View File

@ -57,6 +57,7 @@ import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity;
import org.sufficientlysecure.keychain.ui.ViewKeyActivity; import org.sufficientlysecure.keychain.ui.ViewKeyActivity;
import org.sufficientlysecure.keychain.util.InputData; import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ParcelableCache;
import org.sufficientlysecure.keychain.util.Passphrase; import org.sufficientlysecure.keychain.util.Passphrase;
import java.io.IOException; import java.io.IOException;
@ -67,6 +68,25 @@ import java.util.Set;
public class OpenPgpService extends RemoteService { public class OpenPgpService extends RemoteService {
/**
* Instead of parceling the CryptoInputParcel, they are cached on our side to prevent
* leakage of passphrases, symmetric keys, an yubikey related pass-through values
*/
private static ParcelableCache<CryptoInputParcel> inputParcelCache;
static {
inputParcelCache = new ParcelableCache<>();
}
public static void cacheCryptoInputParcel(Intent data, CryptoInputParcel inputParcel) {
inputParcelCache.cacheAndWriteToIntent(inputParcel, data,
OpenPgpApi.EXTRA_CALL_UUID1, OpenPgpApi.EXTRA_CALL_UUID2);
}
public static CryptoInputParcel getCryptoInputParcel(Intent data) {
return inputParcelCache.readFromIntentAndGetFromCache(data,
OpenPgpApi.EXTRA_CALL_UUID1, OpenPgpApi.EXTRA_CALL_UUID2);
}
static final String[] EMAIL_SEARCH_PROJECTION = new String[]{ static final String[] EMAIL_SEARCH_PROJECTION = new String[]{
KeyRings._ID, KeyRings._ID,
KeyRings.MASTER_KEY_ID, KeyRings.MASTER_KEY_ID,
@ -263,18 +283,19 @@ public class OpenPgpService extends RemoteService {
long inputLength = is.available(); long inputLength = is.available();
InputData inputData = new InputData(is, inputLength); InputData inputData = new InputData(is, inputLength);
CryptoInputParcel cryptoInput = data.getParcelableExtra(OpenPgpApi.EXTRA_CRYPTO_INPUT); CryptoInputParcel inputParcel = getCryptoInputParcel(data);
if (cryptoInput == null) { if (inputParcel == null) {
cryptoInput = new CryptoInputParcel(); inputParcel = new CryptoInputParcel();
} }
// override passphrase in input parcel if given by API call
if (data.hasExtra(OpenPgpApi.EXTRA_PASSPHRASE)) { if (data.hasExtra(OpenPgpApi.EXTRA_PASSPHRASE)) {
cryptoInput = new CryptoInputParcel(cryptoInput.getSignatureTime(), inputParcel = new CryptoInputParcel(inputParcel.getSignatureTime(),
new Passphrase(data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE))); new Passphrase(data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE)));
} }
// execute PGP operation! // execute PGP operation!
PgpSignEncryptOperation pse = new PgpSignEncryptOperation(this, new ProviderHelper(getContext()), null); PgpSignEncryptOperation pse = new PgpSignEncryptOperation(this, new ProviderHelper(getContext()), null);
PgpSignEncryptResult pgpResult = pse.execute(pseInput, cryptoInput, inputData, os); PgpSignEncryptResult pgpResult = pse.execute(pseInput, inputParcel, inputData, os);
if (pgpResult.isPending()) { if (pgpResult.isPending()) {
@ -403,19 +424,20 @@ public class OpenPgpService extends RemoteService {
.setAdditionalEncryptId(signKeyId); // add sign key for encryption .setAdditionalEncryptId(signKeyId); // add sign key for encryption
} }
CryptoInputParcel cryptoInput = data.getParcelableExtra(OpenPgpApi.EXTRA_CRYPTO_INPUT); CryptoInputParcel inputParcel = getCryptoInputParcel(data);
if (cryptoInput == null) { if (inputParcel == null) {
cryptoInput = new CryptoInputParcel(); inputParcel = new CryptoInputParcel();
} }
// override passphrase in input parcel if given by API call
if (data.hasExtra(OpenPgpApi.EXTRA_PASSPHRASE)) { if (data.hasExtra(OpenPgpApi.EXTRA_PASSPHRASE)) {
cryptoInput = new CryptoInputParcel(cryptoInput.getSignatureTime(), inputParcel = new CryptoInputParcel(inputParcel.getSignatureTime(),
new Passphrase(data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE))); new Passphrase(data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE)));
} }
PgpSignEncryptOperation op = new PgpSignEncryptOperation(this, new ProviderHelper(getContext()), null); PgpSignEncryptOperation op = new PgpSignEncryptOperation(this, new ProviderHelper(getContext()), null);
// execute PGP operation! // execute PGP operation!
PgpSignEncryptResult pgpResult = op.execute(pseInput, cryptoInput, inputData, os); PgpSignEncryptResult pgpResult = op.execute(pseInput, inputParcel, inputData, os);
if (pgpResult.isPending()) { if (pgpResult.isPending()) {
RequiredInputParcel requiredInput = pgpResult.getRequiredInputParcel(); RequiredInputParcel requiredInput = pgpResult.getRequiredInputParcel();
@ -491,12 +513,13 @@ public class OpenPgpService extends RemoteService {
this, new ProviderHelper(getContext()), null, inputData, os this, new ProviderHelper(getContext()), null, inputData, os
); );
CryptoInputParcel cryptoInput = data.getParcelableExtra(OpenPgpApi.EXTRA_CRYPTO_INPUT); CryptoInputParcel inputParcel = getCryptoInputParcel(data);
if (cryptoInput == null) { if (inputParcel == null) {
cryptoInput = new CryptoInputParcel(); inputParcel = new CryptoInputParcel();
} }
// override passphrase in input parcel if given by API call
if (data.hasExtra(OpenPgpApi.EXTRA_PASSPHRASE)) { if (data.hasExtra(OpenPgpApi.EXTRA_PASSPHRASE)) {
cryptoInput = new CryptoInputParcel(cryptoInput.getSignatureTime(), inputParcel = new CryptoInputParcel(inputParcel.getSignatureTime(),
new Passphrase(data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE))); new Passphrase(data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE)));
} }
@ -509,7 +532,7 @@ public class OpenPgpService extends RemoteService {
.setDecryptMetadataOnly(decryptMetadataOnly) .setDecryptMetadataOnly(decryptMetadataOnly)
.setDetachedSignature(detachedSignature); .setDetachedSignature(detachedSignature);
DecryptVerifyResult pgpResult = builder.build().execute(cryptoInput); DecryptVerifyResult pgpResult = builder.build().execute(inputParcel);
if (pgpResult.isPending()) { if (pgpResult.isPending()) {
// prepare and return PendingIntent to be executed by client // prepare and return PendingIntent to be executed by client

View File

@ -16,6 +16,7 @@ import android.view.WindowManager;
import org.openintents.openpgp.util.OpenPgpApi; import org.openintents.openpgp.util.OpenPgpApi;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.remote.OpenPgpService;
import org.sufficientlysecure.keychain.service.PassphraseCacheService; import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel; import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
@ -69,7 +70,7 @@ public class NfcOperationActivity extends BaseNfcActivity {
@Override @Override
protected void onNfcPerform() throws IOException { protected void onNfcPerform() throws IOException {
CryptoInputParcel resultData = new CryptoInputParcel(mRequiredInput.mSignatureTime); CryptoInputParcel inputParcel = new CryptoInputParcel(mRequiredInput.mSignatureTime);
switch (mRequiredInput.mType) { switch (mRequiredInput.mType) {
@ -77,7 +78,7 @@ public class NfcOperationActivity extends BaseNfcActivity {
for (int i = 0; i < mRequiredInput.mInputHashes.length; i++) { for (int i = 0; i < mRequiredInput.mInputHashes.length; i++) {
byte[] hash = mRequiredInput.mInputHashes[i]; byte[] hash = mRequiredInput.mInputHashes[i];
byte[] decryptedSessionKey = nfcDecryptSessionKey(hash); byte[] decryptedSessionKey = nfcDecryptSessionKey(hash);
resultData.addCryptoData(hash, decryptedSessionKey); inputParcel.addCryptoData(hash, decryptedSessionKey);
} }
break; break;
@ -86,17 +87,17 @@ public class NfcOperationActivity extends BaseNfcActivity {
byte[] hash = mRequiredInput.mInputHashes[i]; byte[] hash = mRequiredInput.mInputHashes[i];
int algo = mRequiredInput.mSignAlgos[i]; int algo = mRequiredInput.mSignAlgos[i];
byte[] signedHash = nfcCalculateSignature(hash, algo); byte[] signedHash = nfcCalculateSignature(hash, algo);
resultData.addCryptoData(hash, signedHash); inputParcel.addCryptoData(hash, signedHash);
} }
break; break;
} }
if (mServiceIntent != null) { if (mServiceIntent != null) {
mServiceIntent.putExtra(OpenPgpApi.EXTRA_CRYPTO_INPUT, resultData); OpenPgpService.cacheCryptoInputParcel(mServiceIntent, inputParcel);
setResult(RESULT_OK, mServiceIntent); setResult(RESULT_OK, mServiceIntent);
} else { } else {
Intent result = new Intent(); Intent result = new Intent();
result.putExtra(NfcOperationActivity.RESULT_DATA, resultData); result.putExtra(NfcOperationActivity.RESULT_DATA, inputParcel);
setResult(RESULT_OK, result); setResult(RESULT_OK, result);
} }

View File

@ -53,6 +53,7 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing; import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.remote.OpenPgpService;
import org.sufficientlysecure.keychain.service.PassphraseCacheService; import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel; import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
@ -425,14 +426,14 @@ public class PassphraseDialogActivity extends FragmentActivity {
return; return;
} }
CryptoInputParcel inputParcel = new CryptoInputParcel(null, passphrase);
if (mServiceIntent != null) { if (mServiceIntent != null) {
// TODO really pass this through the PendingIntent? OpenPgpService.cacheCryptoInputParcel(mServiceIntent, inputParcel);
mServiceIntent.putExtra(OpenPgpApi.EXTRA_CRYPTO_INPUT, new CryptoInputParcel(null, passphrase));
getActivity().setResult(RESULT_OK, mServiceIntent); getActivity().setResult(RESULT_OK, mServiceIntent);
} else { } else {
// also return passphrase back to activity // also return passphrase back to activity
Intent returnIntent = new Intent(); Intent returnIntent = new Intent();
returnIntent.putExtra(RESULT_CRYPTO_INPUT, new CryptoInputParcel(null, passphrase)); returnIntent.putExtra(RESULT_CRYPTO_INPUT, inputParcel);
getActivity().setResult(RESULT_OK, returnIntent); getActivity().setResult(RESULT_OK, returnIntent);
} }

View File

@ -17,6 +17,8 @@
package org.sufficientlysecure.keychain.util; package org.sufficientlysecure.keychain.util;
import android.content.Intent;
import android.os.Bundle;
import android.os.Parcel; import android.os.Parcel;
import java.util.UUID; import java.util.UUID;
@ -75,6 +77,17 @@ public class ParcelableCache<E extends Object> {
} }
} }
public E readFromIntentAndGetFromCache(Intent data, String key1, String key2) {
if (!data.getExtras().containsKey(key1) || !data.getExtras().containsKey(key2)) {
return null;
}
long mostSig = data.getLongExtra(key1, 0);
long leastSig = data.getLongExtra(key2, 0);
UUID mTicket = new UUID(mostSig, leastSig);
// fetch the dehydrated log out of storage (this removes it from the dehydration pool)
return rehydrateParcelable(mTicket);
}
public E readFromParcelAndGetFromCache(Parcel source) { public E readFromParcelAndGetFromCache(Parcel source) {
long mostSig = source.readLong(); long mostSig = source.readLong();
long leastSig = source.readLong(); long leastSig = source.readLong();
@ -83,7 +96,6 @@ public class ParcelableCache<E extends Object> {
return rehydrateParcelable(mTicket); return rehydrateParcelable(mTicket);
} }
public void cacheAndWriteToParcel(E parcelable, Parcel dest) { public void cacheAndWriteToParcel(E parcelable, Parcel dest) {
// Get a ticket for our log. // Get a ticket for our log.
UUID mTicket = dehydrateParcelable(parcelable); UUID mTicket = dehydrateParcelable(parcelable);
@ -92,4 +104,12 @@ public class ParcelableCache<E extends Object> {
dest.writeLong(mTicket.getLeastSignificantBits()); dest.writeLong(mTicket.getLeastSignificantBits());
} }
public void cacheAndWriteToIntent(E parcelable, Intent data, String key1, String key2) {
// Get a ticket for our log.
UUID mTicket = dehydrateParcelable(parcelable);
// And write out the UUID most and least significant bits.
data.putExtra(key1, mTicket.getMostSignificantBits());
data.putExtra(key2, mTicket.getLeastSignificantBits());
}
} }