mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-13 19:29:06 +01:00
Encrypt bundles in case the OS writes them to unencrypted storage.
Only the bundle contents created by Briar classes are encrypted.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package net.sf.briar.android;
|
||||
|
||||
import net.sf.briar.api.android.AndroidExecutor;
|
||||
import net.sf.briar.api.android.BundleEncrypter;
|
||||
import net.sf.briar.api.android.ReferenceManager;
|
||||
|
||||
import com.google.inject.AbstractModule;
|
||||
@@ -11,6 +12,8 @@ public class AndroidModule extends AbstractModule {
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(AndroidExecutor.class).to(AndroidExecutorImpl.class);
|
||||
bind(BundleEncrypter.class).to(BundleEncrypterImpl.class).in(
|
||||
Singleton.class);
|
||||
bind(ReferenceManager.class).to(ReferenceManagerImpl.class).in(
|
||||
Singleton.class);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
package net.sf.briar.android;
|
||||
|
||||
import static javax.crypto.Cipher.DECRYPT_MODE;
|
||||
import static javax.crypto.Cipher.ENCRYPT_MODE;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.SecureRandom;
|
||||
|
||||
import net.sf.briar.api.android.BundleEncrypter;
|
||||
import net.sf.briar.api.crypto.AuthenticatedCipher;
|
||||
import net.sf.briar.api.crypto.CryptoComponent;
|
||||
import net.sf.briar.api.crypto.ErasableKey;
|
||||
import net.sf.briar.util.ByteUtils;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcel;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
// This class is not thread-safe
|
||||
class BundleEncrypterImpl implements BundleEncrypter {
|
||||
|
||||
private final AuthenticatedCipher cipher;
|
||||
private final SecureRandom random;
|
||||
private final ErasableKey key;
|
||||
private final int blockSize, macLength;
|
||||
|
||||
@Inject
|
||||
BundleEncrypterImpl(CryptoComponent crypto) {
|
||||
cipher = crypto.getBundleCipher();
|
||||
random = crypto.getSecureRandom();
|
||||
key = crypto.generateSecretKey();
|
||||
blockSize = cipher.getBlockSize();
|
||||
macLength = cipher.getMacLength();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encrypt(Bundle b) {
|
||||
// Marshall the plaintext contents into a byte array
|
||||
Parcel p = Parcel.obtain();
|
||||
b.writeToParcel(p, 0);
|
||||
byte[] plaintext = p.marshall();
|
||||
p.recycle();
|
||||
// Encrypt the byte array using the storage key and a random IV
|
||||
byte[] iv = new byte[blockSize];
|
||||
random.nextBytes(iv);
|
||||
byte[] ciphertext = new byte[plaintext.length + macLength];
|
||||
try {
|
||||
cipher.init(ENCRYPT_MODE, key, iv, null);
|
||||
cipher.doFinal(plaintext, 0, plaintext.length, ciphertext, 0);
|
||||
} catch(GeneralSecurityException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
ByteUtils.erase(plaintext);
|
||||
// Replace the plaintext contents with the IV and the ciphertext
|
||||
b.clear();
|
||||
b.putByteArray("net.sf.briar.IV", iv);
|
||||
b.putByteArray("net.sf.briar.CIPHERTEXT", ciphertext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean decrypt(Bundle b) {
|
||||
// Retrieve the IV and the ciphertext
|
||||
byte[] iv = b.getByteArray("net.sf.briar.IV");
|
||||
if(iv == null) throw new IllegalArgumentException();
|
||||
if(iv.length != blockSize) throw new IllegalArgumentException();
|
||||
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
|
||||
byte[] plaintext = new byte[ciphertext.length - macLength];
|
||||
try {
|
||||
cipher.init(DECRYPT_MODE, key, iv, null);
|
||||
cipher.doFinal(ciphertext, 0, ciphertext.length, plaintext, 0);
|
||||
} catch(GeneralSecurityException e) {
|
||||
return false; // Invalid ciphertext
|
||||
}
|
||||
// Unmarshall the byte array
|
||||
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");
|
||||
p.setDataPosition(0);
|
||||
b.readFromParcel(p);
|
||||
p.recycle();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -28,8 +28,8 @@ implements OnClickListener {
|
||||
Logger.getLogger(HelloWorldActivity.class.getName());
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
public void onCreate(Bundle state) {
|
||||
super.onCreate(state);
|
||||
if(LOG.isLoggable(INFO)) LOG.info("Created");
|
||||
LinearLayout layout = new LinearLayout(this);
|
||||
layout.setLayoutParams(new LayoutParams(MATCH_PARENT, MATCH_PARENT));
|
||||
|
||||
@@ -5,6 +5,7 @@ import static java.util.logging.Level.WARNING;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import net.sf.briar.api.android.BundleEncrypter;
|
||||
import net.sf.briar.api.android.ReferenceManager;
|
||||
import net.sf.briar.api.crypto.CryptoComponent;
|
||||
import net.sf.briar.api.db.DatabaseComponent;
|
||||
@@ -30,6 +31,7 @@ implements InvitationListener {
|
||||
@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;
|
||||
@@ -46,6 +48,7 @@ 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
|
||||
setView(new NetworkSetupView(this));
|
||||
@@ -120,14 +123,16 @@ implements InvitationListener {
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle state) {
|
||||
super.onSaveInstanceState(state);
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -17,6 +17,8 @@ import android.widget.TextView.OnEditorActionListener;
|
||||
public class ContactAddedView extends AddContactView implements OnClickListener,
|
||||
OnEditorActionListener {
|
||||
|
||||
EditText nicknameEntry = null;
|
||||
|
||||
ContactAddedView(Context ctx) {
|
||||
super(ctx);
|
||||
}
|
||||
@@ -50,7 +52,7 @@ OnEditorActionListener {
|
||||
innerLayout.setGravity(CENTER);
|
||||
|
||||
final Button done = new Button(ctx);
|
||||
EditText nicknameEntry = new EditText(ctx) {
|
||||
nicknameEntry = new EditText(ctx) {
|
||||
@Override
|
||||
protected void onTextChanged(CharSequence text, int start,
|
||||
int lengthBefore, int lengthAfter) {
|
||||
@@ -71,11 +73,13 @@ OnEditorActionListener {
|
||||
}
|
||||
|
||||
public boolean onEditorAction(TextView textView, int actionId, KeyEvent e) {
|
||||
if(textView.getText().length() > 0) container.finish();
|
||||
String nickname = textView.getText().toString();
|
||||
if(nickname.length() > 0) container.addContactAndFinish(nickname);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void onClick(View view) {
|
||||
container.finish(); // Done
|
||||
String nickname = nicknameEntry.getText().toString();
|
||||
container.addContactAndFinish(nickname);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user