mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 10:49:06 +01:00
UI for setting and entering the password that encrypts the database key.
This commit is contained in:
@@ -30,7 +30,7 @@
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".android.SetupActivity"
|
||||
android:label="@string/app_name" >
|
||||
android:label="@string/setup_title" >
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".android.SplashScreenActivity"
|
||||
|
||||
@@ -3,6 +3,13 @@
|
||||
<string name="app_name">Briar</string>
|
||||
<string name="notification_title">Briar is running</string>
|
||||
<string name="notification_text">Touch to quit.</string>
|
||||
<string name="setup_title">Briar Setup</string>
|
||||
<string name="choose_nickname">Choose your nickname:</string>
|
||||
<string name="choose_password">Choose your password:</string>
|
||||
<string name="confirm_password">Confirm your password:</string>
|
||||
<string name="format_min_password">Password must be at least %1$d characters long.</string>
|
||||
<string name="enter_password">Enter your password:</string>
|
||||
<string name="try_again">Wrong password, try again</string>
|
||||
<string name="contact_list_button">Contacts</string>
|
||||
<string name="messages_button">Messages</string>
|
||||
<string name="groups_button">Groups</string>
|
||||
@@ -29,14 +36,14 @@
|
||||
<string name="format_connecting_wifi">Connecting via %1$s\u2026</string>
|
||||
<string name="connecting_bluetooth">Connecting via Bluetooth\u2026</string>
|
||||
<string name="connection_failed">Connection failed</string>
|
||||
<string name="check_same_network">Please check that you are both using the same network.</string>
|
||||
<string name="check_same_network">Please check that you are both using the same network</string>
|
||||
<string name="try_again_button">Try again</string>
|
||||
<string name="connected_to_contact">Connected to contact</string>
|
||||
<string name="your_confirmation_code">Your confirmation code is</string>
|
||||
<string name="enter_confirmation_code">Please enter your contact\'s confirmation code:</string>
|
||||
<string name="waiting_for_contact">Waiting for contact\u2026</string>
|
||||
<string name="codes_do_not_match">Codes do not match</string>
|
||||
<string name="interfering">This could mean that someone is trying to interfere with your connection.</string>
|
||||
<string name="interfering">This could mean that someone is trying to interfere with your connection</string>
|
||||
<string name="contact_added">Contact added</string>
|
||||
<string name="done_button">Done</string>
|
||||
<string name="messages_title">Messages</string>
|
||||
@@ -67,7 +74,6 @@
|
||||
<string name="new_blog_item">New blog\u2026</string>
|
||||
<string name="create_nickname_item">New nickname\u2026</string>
|
||||
<string name="create_identity_title">Create an Identity</string>
|
||||
<string name="choose_nickname">Choose your nickname:</string>
|
||||
<string name="create_button">Create</string>
|
||||
<string name="no_contacts">You don\'t have any contacts. Add a contact now?</string>
|
||||
<string name="add_button">Add</string>
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
package net.sf.briar.android;
|
||||
|
||||
import static android.text.InputType.TYPE_CLASS_TEXT;
|
||||
import static android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD;
|
||||
import static android.view.Gravity.CENTER;
|
||||
import static android.view.Gravity.CENTER_HORIZONTAL;
|
||||
import static android.view.View.GONE;
|
||||
import static android.view.View.VISIBLE;
|
||||
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
import static android.view.inputmethod.InputMethodManager.HIDE_IMPLICIT_ONLY;
|
||||
import static android.widget.LinearLayout.VERTICAL;
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static net.sf.briar.android.widgets.CommonLayoutParams.MATCH_MATCH;
|
||||
import static net.sf.briar.android.widgets.CommonLayoutParams.WRAP_WRAP;
|
||||
import static net.sf.briar.api.messaging.Rating.GOOD;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -22,20 +30,31 @@ import net.sf.briar.android.messages.ConversationListActivity;
|
||||
import net.sf.briar.api.LocalAuthor;
|
||||
import net.sf.briar.api.android.DatabaseUiExecutor;
|
||||
import net.sf.briar.api.android.ReferenceManager;
|
||||
import net.sf.briar.api.crypto.CryptoComponent;
|
||||
import net.sf.briar.api.crypto.CryptoExecutor;
|
||||
import net.sf.briar.api.db.DatabaseComponent;
|
||||
import net.sf.briar.api.db.DatabaseConfig;
|
||||
import net.sf.briar.api.db.DbException;
|
||||
import net.sf.briar.util.StringUtils;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.text.Editable;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.GridView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ListView;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.TextView.OnEditorActionListener;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
@@ -48,10 +67,17 @@ public class HomeScreenActivity extends BriarActivity {
|
||||
new BriarServiceConnection();
|
||||
|
||||
@Inject private ReferenceManager referenceManager = null;
|
||||
@Inject private DatabaseConfig databaseConfig = null;
|
||||
@Inject @DatabaseUiExecutor private Executor dbUiExecutor = null;
|
||||
@Inject @CryptoExecutor private Executor cryptoExecutor = null;
|
||||
private boolean bound = false;
|
||||
private TextView tryAgain = null;
|
||||
private Button continueButton = null;
|
||||
private ProgressBar progress = null;
|
||||
|
||||
// Fields that are accessed from background threads must be volatile
|
||||
@Inject private volatile DatabaseComponent db = null;
|
||||
@Inject private volatile CryptoComponent crypto = null;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle state) {
|
||||
@@ -62,20 +88,23 @@ public class HomeScreenActivity extends BriarActivity {
|
||||
if(quit) {
|
||||
// The activity was launched from the notification bar
|
||||
showSpinner();
|
||||
bindService();
|
||||
quit();
|
||||
} else if(handle != -1) {
|
||||
// The activity was launched from the setup wizard
|
||||
showSpinner();
|
||||
startService(new Intent(BriarService.class.getName()));
|
||||
bindService();
|
||||
storeLocalAuthor(referenceManager.removeReference(handle,
|
||||
LocalAuthor.class));
|
||||
} else {
|
||||
} else if(databaseConfig.getEncryptionKey() == null) {
|
||||
// The activity was launched from the splash screen
|
||||
showPasswordPrompt();
|
||||
} else {
|
||||
// The activity has been launched before
|
||||
showButtons();
|
||||
bindService();
|
||||
}
|
||||
// Start the service and bind to it
|
||||
startService(new Intent(BriarService.class.getName()));
|
||||
bindService(new Intent(BriarService.class.getName()),
|
||||
serviceConnection, 0);
|
||||
}
|
||||
|
||||
private void showSpinner() {
|
||||
@@ -88,6 +117,11 @@ public class HomeScreenActivity extends BriarActivity {
|
||||
setContentView(layout);
|
||||
}
|
||||
|
||||
private void bindService() {
|
||||
bound = bindService(new Intent(BriarService.class.getName()),
|
||||
serviceConnection, 0);
|
||||
}
|
||||
|
||||
private void quit() {
|
||||
new Thread() {
|
||||
@Override
|
||||
@@ -145,6 +179,110 @@ public class HomeScreenActivity extends BriarActivity {
|
||||
});
|
||||
}
|
||||
|
||||
private void showPasswordPrompt() {
|
||||
SharedPreferences prefs = getSharedPreferences("db", MODE_PRIVATE);
|
||||
String hex = prefs.getString("key", null);
|
||||
if(hex == null) throw new IllegalStateException();
|
||||
final byte[] encrypted = StringUtils.fromHexString(hex);
|
||||
|
||||
LinearLayout layout = new LinearLayout(this);
|
||||
layout.setLayoutParams(MATCH_MATCH);
|
||||
layout.setOrientation(VERTICAL);
|
||||
layout.setGravity(CENTER_HORIZONTAL);
|
||||
|
||||
TextView enterPassword = new TextView(this);
|
||||
enterPassword.setGravity(CENTER);
|
||||
enterPassword.setTextSize(18);
|
||||
enterPassword.setPadding(10, 10, 10, 10);
|
||||
enterPassword.setText(R.string.enter_password);
|
||||
layout.addView(enterPassword);
|
||||
|
||||
final EditText passwordEntry = new EditText(this);
|
||||
passwordEntry.setMaxLines(1);
|
||||
passwordEntry.setPadding(10, 0, 10, 10);
|
||||
int inputType = TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_PASSWORD;
|
||||
passwordEntry.setInputType(inputType);
|
||||
passwordEntry.setOnEditorActionListener(new OnEditorActionListener() {
|
||||
public boolean onEditorAction(TextView v, int action, KeyEvent e) {
|
||||
validatePassword(encrypted, passwordEntry.getText());
|
||||
return true;
|
||||
}
|
||||
});
|
||||
layout.addView(passwordEntry);
|
||||
|
||||
tryAgain = new TextView(this);
|
||||
tryAgain.setGravity(CENTER);
|
||||
tryAgain.setTextSize(14);
|
||||
tryAgain.setPadding(10, 10, 10, 10);
|
||||
tryAgain.setText(R.string.try_again);
|
||||
tryAgain.setVisibility(GONE);
|
||||
layout.addView(tryAgain);
|
||||
|
||||
continueButton = new Button(this);
|
||||
continueButton.setLayoutParams(WRAP_WRAP);
|
||||
continueButton.setText(R.string.continue_button);
|
||||
continueButton.setOnClickListener(new OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
validatePassword(encrypted, passwordEntry.getText());
|
||||
}
|
||||
});
|
||||
layout.addView(continueButton);
|
||||
|
||||
progress = new ProgressBar(this);
|
||||
progress.setLayoutParams(WRAP_WRAP);
|
||||
progress.setIndeterminate(true);
|
||||
progress.setVisibility(GONE);
|
||||
layout.addView(progress);
|
||||
setContentView(layout);
|
||||
}
|
||||
|
||||
private void validatePassword(final byte[] encrypted, Editable e) {
|
||||
if(tryAgain == null || continueButton == null || progress == null)
|
||||
return;
|
||||
// Hide the soft keyboard
|
||||
Object o = getSystemService(INPUT_METHOD_SERVICE);
|
||||
((InputMethodManager) o).toggleSoftInput(HIDE_IMPLICIT_ONLY, 0);
|
||||
// Replace the button with a progress bar
|
||||
continueButton.setVisibility(GONE);
|
||||
progress.setVisibility(VISIBLE);
|
||||
// Decrypt the database key in a background thread
|
||||
int length = e.length();
|
||||
final char[] password = new char[length];
|
||||
e.getChars(0, length, password, 0);
|
||||
e.delete(0, length);
|
||||
cryptoExecutor.execute(new Runnable() {
|
||||
public void run() {
|
||||
byte[] key = crypto.decryptWithPassword(encrypted, password);
|
||||
if(key == null) {
|
||||
tryAgain();
|
||||
} else {
|
||||
databaseConfig.setEncryptionKey(key);
|
||||
showButtonsAndStartService();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void tryAgain() {
|
||||
runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
tryAgain.setVisibility(VISIBLE);
|
||||
continueButton.setVisibility(VISIBLE);
|
||||
progress.setVisibility(GONE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void showButtonsAndStartService() {
|
||||
runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
showButtons();
|
||||
startService(new Intent(BriarService.class.getName()));
|
||||
bindService();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void showButtons() {
|
||||
ListView.LayoutParams matchMatch =
|
||||
new ListView.LayoutParams(MATCH_PARENT, MATCH_PARENT);
|
||||
@@ -227,6 +365,7 @@ public class HomeScreenActivity extends BriarActivity {
|
||||
quitButton.setText(R.string.quit_button);
|
||||
quitButton.setOnClickListener(new OnClickListener() {
|
||||
public void onClick(View view) {
|
||||
showSpinner();
|
||||
quit();
|
||||
}
|
||||
});
|
||||
@@ -264,6 +403,6 @@ public class HomeScreenActivity extends BriarActivity {
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
unbindService(serviceConnection);
|
||||
if(bound) unbindService(serviceConnection);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,17 +3,18 @@ package net.sf.briar.android;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
||||
import static android.text.InputType.TYPE_CLASS_TEXT;
|
||||
import static android.text.InputType.TYPE_TEXT_FLAG_CAP_WORDS;
|
||||
import static android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD;
|
||||
import static android.view.Gravity.CENTER;
|
||||
import static android.view.Gravity.CENTER_HORIZONTAL;
|
||||
import static android.view.View.GONE;
|
||||
import static android.view.View.VISIBLE;
|
||||
import static android.view.inputmethod.InputMethodManager.HIDE_IMPLICIT_ONLY;
|
||||
import static android.widget.LinearLayout.VERTICAL;
|
||||
import static net.sf.briar.android.widgets.CommonLayoutParams.MATCH_MATCH;
|
||||
import static net.sf.briar.android.widgets.CommonLayoutParams.WRAP_WRAP;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.KeyPair;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import net.sf.briar.R;
|
||||
@@ -22,31 +23,36 @@ import net.sf.briar.api.LocalAuthor;
|
||||
import net.sf.briar.api.android.ReferenceManager;
|
||||
import net.sf.briar.api.crypto.CryptoComponent;
|
||||
import net.sf.briar.api.crypto.CryptoExecutor;
|
||||
import net.sf.briar.api.db.DatabaseConfig;
|
||||
import net.sf.briar.util.StringUtils;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.Editor;
|
||||
import android.os.Bundle;
|
||||
import android.view.KeyEvent;
|
||||
import android.text.Editable;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.TextView.OnEditorActionListener;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
public class SetupActivity extends BriarActivity
|
||||
implements OnEditorActionListener, OnClickListener {
|
||||
public class SetupActivity extends BriarActivity implements OnClickListener {
|
||||
|
||||
private static final int MIN_PASSWORD_LENGTH = 8;
|
||||
|
||||
@Inject @CryptoExecutor private Executor cryptoExecutor;
|
||||
private EditText nicknameEntry = null;
|
||||
private Button createButton = null;
|
||||
private EditText passwordEntry = null, passwordConfirmation = null;
|
||||
private Button continueButton = null;
|
||||
private ProgressBar progress = null;
|
||||
|
||||
// Fields that are accessed from background threads must be volatile
|
||||
@Inject private volatile CryptoComponent crypto;
|
||||
@Inject private volatile DatabaseConfig databaseConfig;
|
||||
@Inject private volatile AuthorFactory authorFactory;
|
||||
@Inject private volatile ReferenceManager referenceManager;
|
||||
|
||||
@@ -69,24 +75,66 @@ implements OnEditorActionListener, OnClickListener {
|
||||
@Override
|
||||
protected void onTextChanged(CharSequence text, int start,
|
||||
int lengthBefore, int lengthAfter) {
|
||||
if(createButton != null)
|
||||
createButton.setEnabled(lengthAfter > 0);
|
||||
enableOrDisableContinueButton();
|
||||
}
|
||||
};
|
||||
nicknameEntry.setTextSize(18);
|
||||
nicknameEntry.setMaxLines(1);
|
||||
nicknameEntry.setPadding(10, 10, 10, 10);
|
||||
int inputType = TYPE_CLASS_TEXT | TYPE_TEXT_FLAG_CAP_WORDS;
|
||||
nicknameEntry.setInputType(inputType);
|
||||
nicknameEntry.setOnEditorActionListener(this);
|
||||
layout.addView(nicknameEntry);
|
||||
|
||||
createButton = new Button(this);
|
||||
createButton.setLayoutParams(WRAP_WRAP);
|
||||
createButton.setText(R.string.create_button);
|
||||
createButton.setEnabled(false);
|
||||
createButton.setOnClickListener(this);
|
||||
layout.addView(createButton);
|
||||
TextView choosePassword = new TextView(this);
|
||||
choosePassword.setGravity(CENTER);
|
||||
choosePassword.setTextSize(18);
|
||||
choosePassword.setPadding(10, 10, 10, 10);
|
||||
choosePassword.setText(R.string.choose_password);
|
||||
layout.addView(choosePassword);
|
||||
|
||||
passwordEntry = new EditText(this) {
|
||||
@Override
|
||||
protected void onTextChanged(CharSequence text, int start,
|
||||
int lengthBefore, int lengthAfter) {
|
||||
enableOrDisableContinueButton();
|
||||
}
|
||||
};
|
||||
passwordEntry.setMaxLines(1);
|
||||
inputType = TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_PASSWORD;
|
||||
passwordEntry.setInputType(inputType);
|
||||
layout.addView(passwordEntry);
|
||||
|
||||
TextView confirmPassword = new TextView(this);
|
||||
confirmPassword.setGravity(CENTER);
|
||||
confirmPassword.setTextSize(18);
|
||||
confirmPassword.setPadding(10, 10, 10, 10);
|
||||
confirmPassword.setText(R.string.confirm_password);
|
||||
layout.addView(confirmPassword);
|
||||
|
||||
passwordConfirmation = new EditText(this) {
|
||||
@Override
|
||||
protected void onTextChanged(CharSequence text, int start,
|
||||
int lengthBefore, int lengthAfter) {
|
||||
enableOrDisableContinueButton();
|
||||
}
|
||||
};
|
||||
passwordConfirmation.setMaxLines(1);
|
||||
inputType = TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_PASSWORD;
|
||||
passwordConfirmation.setInputType(inputType);
|
||||
layout.addView(passwordConfirmation);
|
||||
|
||||
TextView minPasswordLength = new TextView(this);
|
||||
minPasswordLength.setGravity(CENTER);
|
||||
minPasswordLength.setTextSize(14);
|
||||
minPasswordLength.setPadding(10, 10, 10, 10);
|
||||
String format = getResources().getString(R.string.format_min_password);
|
||||
minPasswordLength.setText(String.format(format, MIN_PASSWORD_LENGTH));
|
||||
layout.addView(minPasswordLength);
|
||||
|
||||
continueButton = new Button(this);
|
||||
continueButton.setLayoutParams(WRAP_WRAP);
|
||||
continueButton.setText(R.string.continue_button);
|
||||
continueButton.setEnabled(false);
|
||||
continueButton.setOnClickListener(this);
|
||||
layout.addView(continueButton);
|
||||
|
||||
progress = new ProgressBar(this);
|
||||
progress.setLayoutParams(WRAP_WRAP);
|
||||
@@ -97,20 +145,42 @@ implements OnEditorActionListener, OnClickListener {
|
||||
setContentView(layout);
|
||||
}
|
||||
|
||||
public boolean onEditorAction(TextView textView, int actionId, KeyEvent e) {
|
||||
validateNickname();
|
||||
return true;
|
||||
private void enableOrDisableContinueButton() {
|
||||
if(nicknameEntry == null || passwordEntry == null ||
|
||||
passwordConfirmation == null || continueButton == null) return;
|
||||
boolean nicknameNotEmpty = nicknameEntry.getText().length() > 0;
|
||||
char[] firstPassword = getChars(passwordEntry.getText());
|
||||
char[] secondPassword = getChars(passwordConfirmation.getText());
|
||||
boolean passwordLength = firstPassword.length >= MIN_PASSWORD_LENGTH;
|
||||
boolean passwordsMatch = Arrays.equals(firstPassword, secondPassword);
|
||||
for(int i = 0; i < firstPassword.length; i++) firstPassword[i] = 0;
|
||||
for(int i = 0; i < secondPassword.length; i++) secondPassword[i] = 0;
|
||||
boolean valid = nicknameNotEmpty && passwordLength && passwordsMatch;
|
||||
continueButton.setEnabled(valid);
|
||||
}
|
||||
|
||||
private char[] getChars(Editable e) {
|
||||
int length = e.length();
|
||||
char[] c = new char[length];
|
||||
e.getChars(0, length, c, 0);
|
||||
return c;
|
||||
}
|
||||
|
||||
public void onClick(View view) {
|
||||
if(!validateNickname()) return;
|
||||
final String nickname = nicknameEntry.getText().toString();
|
||||
final char[] password = getChars(passwordEntry.getText());
|
||||
delete(passwordEntry.getText());
|
||||
delete(passwordConfirmation.getText());
|
||||
// Replace the button with a progress bar
|
||||
createButton.setVisibility(GONE);
|
||||
continueButton.setVisibility(GONE);
|
||||
progress.setVisibility(VISIBLE);
|
||||
// Create the identity in a background thread
|
||||
// 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[] encrypted = crypto.encryptWithPassword(key, password);
|
||||
storeEncryptedDatabaseKey(encrypted);
|
||||
databaseConfig.setEncryptionKey(key);
|
||||
KeyPair keyPair = crypto.generateSignatureKeyPair();
|
||||
final byte[] publicKey = keyPair.getPublic().getEncoded();
|
||||
final byte[] privateKey = keyPair.getPrivate().getEncoded();
|
||||
@@ -127,6 +197,17 @@ implements OnEditorActionListener, OnClickListener {
|
||||
});
|
||||
}
|
||||
|
||||
private void delete(Editable e) {
|
||||
e.delete(0, e.length());
|
||||
}
|
||||
|
||||
private void storeEncryptedDatabaseKey(byte[] encrypted) {
|
||||
SharedPreferences prefs = getSharedPreferences("db", MODE_PRIVATE);
|
||||
Editor editor = prefs.edit();
|
||||
editor.putString("key", StringUtils.toHexString(encrypted));
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
private void showHomeScreen(final long handle) {
|
||||
runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
@@ -139,12 +220,4 @@ implements OnEditorActionListener, OnClickListener {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean validateNickname() {
|
||||
if(nicknameEntry.getText().toString().equals("")) return false;
|
||||
// Hide the soft keyboard
|
||||
Object o = getSystemService(INPUT_METHOD_SERVICE);
|
||||
((InputMethodManager) o).toggleSoftInput(HIDE_IMPLICIT_ONLY, 0);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package net.sf.briar.android;
|
||||
|
||||
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
||||
import static android.view.Gravity.CENTER;
|
||||
import net.sf.briar.android.widgets.CommonLayoutParams;
|
||||
import static net.sf.briar.android.widgets.CommonLayoutParams.MATCH_MATCH;
|
||||
import net.sf.briar.api.db.DatabaseConfig;
|
||||
import roboguice.RoboGuice;
|
||||
import roboguice.activity.RoboSplashActivity;
|
||||
@@ -24,7 +24,7 @@ public class SplashScreenActivity extends RoboSplashActivity {
|
||||
public void onCreate(Bundle state) {
|
||||
super.onCreate(null);
|
||||
LinearLayout layout = new LinearLayout(this);
|
||||
layout.setLayoutParams(CommonLayoutParams.MATCH_MATCH);
|
||||
layout.setLayoutParams(MATCH_MATCH);
|
||||
layout.setGravity(CENTER);
|
||||
ProgressBar spinner = new ProgressBar(this);
|
||||
spinner.setIndeterminate(true);
|
||||
|
||||
@@ -99,9 +99,8 @@ SelectContactsDialog.Listener {
|
||||
enableOrDisableCreateButton();
|
||||
}
|
||||
};
|
||||
nameEntry.setTextSize(18);
|
||||
nameEntry.setMaxLines(1);
|
||||
nameEntry.setPadding(10, 10, 10, 10);
|
||||
nameEntry.setPadding(10, 0, 10, 10);
|
||||
nameEntry.setInputType(TYPE_CLASS_TEXT | TYPE_TEXT_FLAG_CAP_SENTENCES);
|
||||
nameEntry.setOnEditorActionListener(this);
|
||||
layout.addView(nameEntry);
|
||||
|
||||
@@ -94,9 +94,8 @@ SelectContactsDialog.Listener {
|
||||
enableOrDisableCreateButton();
|
||||
}
|
||||
};
|
||||
nameEntry.setTextSize(18);
|
||||
nameEntry.setMaxLines(1);
|
||||
nameEntry.setPadding(10, 10, 10, 10);
|
||||
nameEntry.setPadding(10, 0, 10, 10);
|
||||
nameEntry.setInputType(TYPE_CLASS_TEXT | TYPE_TEXT_FLAG_CAP_SENTENCES);
|
||||
nameEntry.setOnEditorActionListener(this);
|
||||
layout.addView(nameEntry);
|
||||
|
||||
@@ -35,6 +35,8 @@ public class HelloWorldModule extends AbstractModule {
|
||||
final File dir = app.getApplicationContext().getDir("db", MODE_PRIVATE);
|
||||
return new DatabaseConfig() {
|
||||
|
||||
private volatile byte[] key = null;
|
||||
|
||||
public boolean databaseExists() {
|
||||
return dir.isDirectory() && dir.listFiles().length > 0;
|
||||
}
|
||||
@@ -43,8 +45,12 @@ public class HelloWorldModule extends AbstractModule {
|
||||
return dir;
|
||||
}
|
||||
|
||||
public char[] getPassword() {
|
||||
return "foo bar".toCharArray();
|
||||
public void setEncryptionKey(byte[] key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public byte[] getEncryptionKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public long getMaxSize() {
|
||||
|
||||
@@ -85,12 +85,11 @@ implements OnEditorActionListener, OnClickListener {
|
||||
protected void onTextChanged(CharSequence text, int start,
|
||||
int lengthBefore, int lengthAfter) {
|
||||
if(createButton != null)
|
||||
createButton.setEnabled(lengthAfter > 0);
|
||||
createButton.setEnabled(getText().length() > 0);
|
||||
}
|
||||
};
|
||||
nicknameEntry.setTextSize(18);
|
||||
nicknameEntry.setMaxLines(1);
|
||||
nicknameEntry.setPadding(10, 10, 10, 10);
|
||||
nicknameEntry.setPadding(10, 0, 10, 10);
|
||||
int inputType = TYPE_CLASS_TEXT | TYPE_TEXT_FLAG_CAP_WORDS;
|
||||
nicknameEntry.setInputType(inputType);
|
||||
nicknameEntry.setOnEditorActionListener(this);
|
||||
|
||||
@@ -96,7 +96,6 @@ implements OnItemSelectedListener, OnClickListener {
|
||||
|
||||
from = new TextView(this);
|
||||
from.setTextSize(18);
|
||||
from.setMaxLines(1);
|
||||
from.setPadding(10, 10, 10, 10);
|
||||
from.setText(R.string.from);
|
||||
header.addView(from);
|
||||
|
||||
@@ -8,7 +8,9 @@ public interface DatabaseConfig {
|
||||
|
||||
File getDatabaseDirectory();
|
||||
|
||||
char[] getPassword();
|
||||
void setEncryptionKey(byte[] key);
|
||||
|
||||
byte[] getEncryptionKey();
|
||||
|
||||
long getMaxSize();
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
private static final String STORAGE_CIPHER_ALGO = "AES/GCM/NoPadding";
|
||||
private static final int STORAGE_IV_BYTES = 16; // 128 bits
|
||||
private static final int PBKDF_SALT_BYTES = 16; // 128 bits
|
||||
private static final int PBKDF_ITERATIONS = 10 * 1000; // FIXME: How many?
|
||||
private static final int PBKDF_ITERATIONS = 1000;
|
||||
private static final String KEY_DERIVATION_ALGO = "AES/CTR/NoPadding";
|
||||
private static final int KEY_DERIVATION_IV_BYTES = 16; // 128 bits
|
||||
|
||||
|
||||
@@ -5,13 +5,13 @@ import java.io.IOException;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Properties;
|
||||
|
||||
import net.sf.briar.api.clock.Clock;
|
||||
import net.sf.briar.api.db.DatabaseConfig;
|
||||
import net.sf.briar.api.db.DbException;
|
||||
import net.sf.briar.util.FileUtils;
|
||||
import net.sf.briar.util.StringUtils;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
@@ -23,23 +23,23 @@ class H2Database extends JdbcDatabase {
|
||||
private static final String COUNTER_TYPE = "INT NOT NULL AUTO_INCREMENT";
|
||||
private static final String SECRET_TYPE = "BINARY(32)";
|
||||
|
||||
private final File dir;
|
||||
private final DatabaseConfig config;
|
||||
private final String url;
|
||||
private final char[] password;
|
||||
private final long maxSize;
|
||||
|
||||
@Inject
|
||||
H2Database(DatabaseConfig config, Clock clock) {
|
||||
super(HASH_TYPE, BINARY_TYPE, COUNTER_TYPE, SECRET_TYPE, config, clock);
|
||||
dir = config.getDatabaseDirectory();
|
||||
url = "jdbc:h2:split:" + new File(dir, "db").getPath()
|
||||
super(HASH_TYPE, BINARY_TYPE, COUNTER_TYPE, SECRET_TYPE, clock);
|
||||
this.config = config;
|
||||
String path = new File(config.getDatabaseDirectory(), "db").getPath();
|
||||
url = "jdbc:h2:split:" + path
|
||||
+ ";CIPHER=AES;MULTI_THREADED=1;DB_CLOSE_ON_EXIT=false";
|
||||
password = config.getPassword();
|
||||
maxSize = config.getMaxSize();
|
||||
}
|
||||
|
||||
public boolean open() throws DbException, IOException {
|
||||
return super.open("org.h2.Driver");
|
||||
boolean reopen = config.databaseExists();
|
||||
if(!reopen) config.getDatabaseDirectory().mkdirs();
|
||||
super.open("org.h2.Driver", reopen);
|
||||
return reopen;
|
||||
}
|
||||
|
||||
public void close() throws DbException {
|
||||
@@ -52,6 +52,8 @@ class H2Database extends JdbcDatabase {
|
||||
}
|
||||
|
||||
public long getFreeSpace() throws DbException {
|
||||
File dir = config.getDatabaseDirectory();
|
||||
long maxSize = config.getMaxSize();
|
||||
try {
|
||||
long free = FileUtils.getFreeSpace(dir);
|
||||
long used = getDiskSpace(dir);
|
||||
@@ -73,14 +75,28 @@ class H2Database extends JdbcDatabase {
|
||||
|
||||
@Override
|
||||
protected Connection createConnection() throws SQLException {
|
||||
char[] passwordCopy = password.clone();
|
||||
char[] password = encodePassword(config.getEncryptionKey());
|
||||
Properties props = new Properties();
|
||||
props.setProperty("user", "user");
|
||||
props.put("password", passwordCopy);
|
||||
props.put("password", password);
|
||||
try {
|
||||
return DriverManager.getConnection(url, props);
|
||||
} finally {
|
||||
Arrays.fill(passwordCopy, (char) 0);
|
||||
for(int i = 0; i < password.length; i++) password[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private char[] encodePassword(byte[] key) {
|
||||
// The database password is the hex-encoded key
|
||||
char[] hex = StringUtils.toHexChars(key);
|
||||
// Separate the database password from the user password with a space
|
||||
char[] user = "password".toCharArray();
|
||||
char[] combined = new char[hex.length + 1 + user.length];
|
||||
System.arraycopy(hex, 0, combined, 0, hex.length);
|
||||
combined[hex.length] = ' ';
|
||||
System.arraycopy(user, 0, combined, hex.length + 1, user.length);
|
||||
// Erase the hex-encoded key
|
||||
for(int i = 0; i < hex.length; i++) hex[i] = 0;
|
||||
return combined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,6 @@ import net.sf.briar.api.TransportConfig;
|
||||
import net.sf.briar.api.TransportId;
|
||||
import net.sf.briar.api.TransportProperties;
|
||||
import net.sf.briar.api.clock.Clock;
|
||||
import net.sf.briar.api.db.DatabaseConfig;
|
||||
import net.sf.briar.api.db.DbClosedException;
|
||||
import net.sf.briar.api.db.DbException;
|
||||
import net.sf.briar.api.db.GroupMessageHeader;
|
||||
@@ -360,7 +359,6 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
|
||||
// Different database libraries use different names for certain types
|
||||
private final String hashType, binaryType, counterType, secretType;
|
||||
private final DatabaseConfig config;
|
||||
private final Clock clock;
|
||||
|
||||
private final LinkedList<Connection> connections =
|
||||
@@ -372,18 +370,16 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
protected abstract Connection createConnection() throws SQLException;
|
||||
|
||||
JdbcDatabase(String hashType, String binaryType, String counterType,
|
||||
String secretType, DatabaseConfig config, Clock clock) {
|
||||
String secretType, Clock clock) {
|
||||
this.hashType = hashType;
|
||||
this.binaryType = binaryType;
|
||||
this.counterType = counterType;
|
||||
this.secretType = secretType;
|
||||
this.config = config;
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
protected boolean open(String driverClass) throws DbException, IOException {
|
||||
boolean reopen = config.databaseExists();
|
||||
if(!reopen) config.getDatabaseDirectory().mkdirs();
|
||||
protected void open(String driverClass, boolean reopen) throws DbException,
|
||||
IOException {
|
||||
// Load the JDBC driver
|
||||
try {
|
||||
Class.forName(driverClass);
|
||||
@@ -399,7 +395,6 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
abortTransaction(txn);
|
||||
throw e;
|
||||
}
|
||||
return reopen;
|
||||
}
|
||||
|
||||
private void createTables(Connection txn) throws DbException {
|
||||
|
||||
@@ -29,16 +29,19 @@ public class StringUtils {
|
||||
else return s;
|
||||
}
|
||||
|
||||
/** Converts the given byte array to a hex character array. */
|
||||
public static char[] toHexChars(byte[] bytes) {
|
||||
char[] hex = new char[bytes.length * 2];
|
||||
for(int i = 0, j = 0; i < bytes.length; i++) {
|
||||
hex[j++] = HEX[(bytes[i] >> 4) & 0xF];
|
||||
hex[j++] = HEX[bytes[i] & 0xF];
|
||||
}
|
||||
return hex;
|
||||
}
|
||||
|
||||
/** Converts the given byte array to a hex string. */
|
||||
public static String toHexString(byte[] bytes) {
|
||||
StringBuilder s = new StringBuilder(bytes.length * 2);
|
||||
for(byte b : bytes) {
|
||||
int high = (b >> 4) & 0xF;
|
||||
s.append(HEX[high]);
|
||||
int low = b & 0xF;
|
||||
s.append(HEX[low]);
|
||||
}
|
||||
return s.toString();
|
||||
return new String(toHexChars(bytes));
|
||||
}
|
||||
|
||||
/** Converts the given hex string to a byte array. */
|
||||
|
||||
@@ -8,6 +8,7 @@ public class TestDatabaseConfig implements DatabaseConfig {
|
||||
|
||||
private final File dir;
|
||||
private final long maxSize;
|
||||
private volatile byte[] key = new byte[] { 'f', 'o', 'o' };
|
||||
|
||||
public TestDatabaseConfig(File dir, long maxSize) {
|
||||
this.dir = dir;
|
||||
@@ -22,8 +23,12 @@ public class TestDatabaseConfig implements DatabaseConfig {
|
||||
return dir;
|
||||
}
|
||||
|
||||
public char[] getPassword() {
|
||||
return "foo bar".toCharArray();
|
||||
public void setEncryptionKey(byte[] key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public byte[] getEncryptionKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public long getMaxSize() {
|
||||
|
||||
Reference in New Issue
Block a user