Use static injection to allow superclass state to be encrypted.

Mutable static fields should be avoided, but this is the only way to
make the bundle encrypter available before calling
RoboActivity.onCreate(), which needs to be passed the decrypted state.
This commit is contained in:
akwizgran
2013-02-19 00:11:10 +00:00
parent 51db9ce1fd
commit f33348ff98
3 changed files with 43 additions and 20 deletions

View File

@@ -1,5 +1,6 @@
package net.sf.briar.android;
import net.sf.briar.android.invitation.AddContactActivity;
import net.sf.briar.api.android.AndroidExecutor;
import net.sf.briar.api.android.BundleEncrypter;
import net.sf.briar.api.android.ReferenceManager;
@@ -16,5 +17,6 @@ public class AndroidModule extends AbstractModule {
Singleton.class);
bind(ReferenceManager.class).to(ReferenceManagerImpl.class).in(
Singleton.class);
requestStaticInjection(AddContactActivity.class);
}
}

View File

@@ -1,10 +1,12 @@
package net.sf.briar.android;
import static java.util.logging.Level.INFO;
import static javax.crypto.Cipher.DECRYPT_MODE;
import static javax.crypto.Cipher.ENCRYPT_MODE;
import java.security.GeneralSecurityException;
import java.security.SecureRandom;
import java.util.logging.Logger;
import net.sf.briar.api.android.BundleEncrypter;
import net.sf.briar.api.crypto.AuthenticatedCipher;
@@ -19,6 +21,9 @@ import com.google.inject.Inject;
// This class is not thread-safe
class BundleEncrypterImpl implements BundleEncrypter {
private static final Logger LOG =
Logger.getLogger(BundleEncrypterImpl.class.getName());
private final AuthenticatedCipher cipher;
private final SecureRandom random;
private final ErasableKey key;
@@ -40,7 +45,11 @@ class BundleEncrypterImpl implements BundleEncrypter {
b.writeToParcel(p, 0);
byte[] plaintext = p.marshall();
p.recycle();
// Encrypt the byte array using the storage key and a random IV
if(LOG.isLoggable(INFO)) {
LOG.info("Marshalled " + b.size() + " mappings, "
+ plaintext.length + " plaintext bytes");
}
// Encrypt the byte array using a random IV
byte[] iv = new byte[blockSize];
random.nextBytes(iv);
byte[] ciphertext = new byte[plaintext.length + macLength];
@@ -66,7 +75,7 @@ class BundleEncrypterImpl implements BundleEncrypter {
byte[] ciphertext = b.getByteArray("net.sf.briar.CIPHERTEXT");
if(ciphertext == null) throw new IllegalArgumentException();
if(ciphertext.length < macLength) throw new IllegalArgumentException();
// Decrypt the ciphertext using the storage key and the IV
// Decrypt the ciphertext using the IV
byte[] plaintext = new byte[ciphertext.length - macLength];
try {
cipher.init(DECRYPT_MODE, key, iv, null);
@@ -78,12 +87,14 @@ class BundleEncrypterImpl implements BundleEncrypter {
Parcel p = Parcel.obtain();
p.unmarshall(plaintext, 0, plaintext.length);
ByteUtils.erase(plaintext);
// Replace the IV and the ciphertext with the plaintext contents
b.remove("net.sf.briar.IV");
b.remove("net.sf.briar.CIPHERTEXT");
// Restore the plaintext contents
p.setDataPosition(0);
b.readFromParcel(p);
p.recycle();
if(LOG.isLoggable(INFO)) {
LOG.info("Unmarshalled " + (b.size() - 2) + " mappings, "
+ plaintext.length + " plaintext bytes");
}
return true;
}
}

View File

@@ -19,6 +19,7 @@ import roboguice.activity.RoboActivity;
import android.os.Bundle;
import com.google.inject.Inject;
import com.google.inject.Provider;
public class AddContactActivity extends RoboActivity
implements InvitationListener {
@@ -26,12 +27,17 @@ implements InvitationListener {
private static final Logger LOG =
Logger.getLogger(AddContactActivity.class.getName());
// This allows us to access bundleEncrypter before calling super.onCreate()
@Inject private static Provider<BundleEncrypter> bundleEncrypterProvider;
private final BundleEncrypter bundleEncrypter =
bundleEncrypterProvider.get();
@Inject private CryptoComponent crypto;
@Inject private DatabaseComponent db;
@Inject @DatabaseExecutor private Executor dbExecutor;
@Inject private InvitationTaskFactory invitationTaskFactory;
@Inject private ReferenceManager referenceManager;
@Inject private BundleEncrypter bundleEncrypter;
// All of the following must be accessed on the UI thread
private AddContactView view = null;
@@ -47,13 +53,13 @@ implements InvitationListener {
@Override
public void onCreate(Bundle state) {
super.onCreate(state);
if(state != null && !bundleEncrypter.decrypt(state)) state = null;
if(state == null) {
// This is a new activity
if(state == null || !bundleEncrypter.decrypt(state)) {
// This is a new activity or the process has restarted
super.onCreate(null);
setView(new NetworkSetupView(this));
} else {
// Restore the activity's state
super.onCreate(state);
networkName = state.getString("net.sf.briar.NETWORK_NAME");
useBluetooth = state.getBoolean("net.sf.briar.USE_BLUETOOTH");
taskHandle = state.getLong("net.sf.briar.TASK_HANDLE", -1);
@@ -114,6 +120,12 @@ implements InvitationListener {
}
}
@Override
public void onRestoreInstanceState(Bundle state) {
if(bundleEncrypter.decrypt(state))
super.onRestoreInstanceState(state);
}
@Override
public void onResume() {
super.onResume();
@@ -123,16 +135,14 @@ implements InvitationListener {
@Override
public void onSaveInstanceState(Bundle state) {
super.onSaveInstanceState(state);
Bundle b = new Bundle();
b.putString("net.sf.briar.NETWORK_NAME", networkName);
b.putBoolean("net.sf.briar.USE_BLUETOOTH", useBluetooth);
b.putInt("net.sf.briar.LOCAL_CODE", localInvitationCode);
b.putInt("net.sf.briar.REMOTE_CODE", remoteInvitationCode);
b.putBoolean("net.sf.briar.FAILED", connectionFailed);
b.putBoolean("net.sf.briar.MATCHED", localMatched && remoteMatched);
if(task != null) b.putLong("net.sf.briar.TASK_HANDLE", taskHandle);
bundleEncrypter.encrypt(b);
state.putAll(b);
state.putString("net.sf.briar.NETWORK_NAME", networkName);
state.putBoolean("net.sf.briar.USE_BLUETOOTH", useBluetooth);
state.putInt("net.sf.briar.LOCAL_CODE", localInvitationCode);
state.putInt("net.sf.briar.REMOTE_CODE", remoteInvitationCode);
state.putBoolean("net.sf.briar.FAILED", connectionFailed);
state.putBoolean("net.sf.briar.MATCHED", localMatched && remoteMatched);
if(task != null) state.putLong("net.sf.briar.TASK_HANDLE", taskHandle);
bundleEncrypter.encrypt(state);
}
@Override