Don't try to erase secrets from memory.

1. The things we're really trying to protect - contact identities,
message contents, etc - can't be erased from memory because they're
encapsulated inside objects we don't control.

2. Long-term secrets can't be protected by erasing them from memory
because they're stored in the database and the database key has to be
held in memory whenever the app's running.

3. If the runtime uses a compacting garbage collector then we have no
way to ensure an object is erased from memory.

4. Trying to erase secrets from memory makes the code more complex.

Conclusion: Let's not try to protect secrets from an attacker who can
read arbitrary memory locations.
This commit is contained in:
akwizgran
2014-12-29 21:08:27 +00:00
parent f316d64afa
commit 358166bc12
28 changed files with 211 additions and 557 deletions

View File

@@ -19,7 +19,6 @@ import static org.briarproject.android.util.CommonLayoutParams.WRAP_WRAP;
import static org.briarproject.api.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
import static org.briarproject.api.crypto.PasswordStrengthEstimator.WEAK;
import java.util.Arrays;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
@@ -43,7 +42,6 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.Bundle;
import android.text.Editable;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnClickListener;
@@ -187,18 +185,16 @@ OnEditorActionListener {
else strengthMeter.setVisibility(INVISIBLE);
String nickname = nicknameEntry.getText().toString();
int nicknameLength = StringUtils.toUtf8(nickname).length;
char[] firstPassword = getChars(passwordEntry.getText());
char[] secondPassword = getChars(passwordConfirmation.getText());
boolean passwordsMatch = Arrays.equals(firstPassword, secondPassword);
String firstPassword = passwordEntry.getText().toString();
String secondPassword = passwordConfirmation.getText().toString();
boolean passwordsMatch = firstPassword.equals(secondPassword);
float strength = strengthEstimator.estimateStrength(firstPassword);
for(int i = 0; i < firstPassword.length; i++) firstPassword[i] = 0;
for(int i = 0; i < secondPassword.length; i++) secondPassword[i] = 0;
strengthMeter.setStrength(strength);
if(nicknameLength > MAX_AUTHOR_NAME_LENGTH) {
feedback.setText(R.string.name_too_long);
} else if(firstPassword.length == 0) {
} else if(firstPassword.length() == 0) {
feedback.setText("");
} else if(secondPassword.length == 0 || passwordsMatch) {
} else if(secondPassword.length() == 0 || passwordsMatch) {
if(strength < PasswordStrengthEstimator.WEAK)
feedback.setText(R.string.password_too_weak);
else feedback.setText("");
@@ -212,13 +208,6 @@ OnEditorActionListener {
&& passwordsMatch && strength >= WEAK);
}
private char[] getChars(Editable e) {
int length = e.length();
char[] c = new char[length];
e.getChars(0, length, c, 0);
return c;
}
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
// Hide the soft keyboard
Object o = getSystemService(INPUT_METHOD_SERVICE);
@@ -231,18 +220,14 @@ OnEditorActionListener {
feedback.setVisibility(GONE);
continueButton.setVisibility(GONE);
progress.setVisibility(VISIBLE);
// Copy the passwords and erase the originals
final String nickname = nicknameEntry.getText().toString();
final char[] password = getChars(passwordEntry.getText());
delete(passwordEntry.getText());
delete(passwordConfirmation.getText());
final String password = passwordEntry.getText().toString();
// Store the DB key and create the identity in a background thread
cryptoExecutor.execute(new Runnable() {
public void run() {
byte[] key = crypto.generateSecretKey().getEncoded();
byte[] key = crypto.generateSecretKey().getBytes();
databaseConfig.setEncryptionKey(key);
byte[] encrypted = encryptDatabaseKey(key, password);
for(int i = 0; i < password.length; i++) password[i] = 0;
storeEncryptedDatabaseKey(encrypted);
LocalAuthor localAuthor = createLocalAuthor(nickname);
showDashboard(referenceManager.putReference(localAuthor,
@@ -251,10 +236,6 @@ OnEditorActionListener {
});
}
private void delete(Editable e) {
e.delete(0, e.length());
}
private void storeEncryptedDatabaseKey(final byte[] encrypted) {
long now = System.currentTimeMillis();
SharedPreferences prefs = getSharedPreferences("db", MODE_PRIVATE);
@@ -266,7 +247,7 @@ OnEditorActionListener {
LOG.info("Key storage took " + duration + " ms");
}
private byte[] encryptDatabaseKey(byte[] key, char[] password) {
private byte[] encryptDatabaseKey(byte[] key, String password) {
long now = System.currentTimeMillis();
byte[] encrypted = crypto.encryptWithPassword(key, password);
long duration = System.currentTimeMillis() - now;