Merge pull request #2381 from nextcloud/showMnemonicBasedOnDeviceCredentials

Show mnemonic based on device credentials
This commit is contained in:
Tobias Kaminsky 2018-07-13 09:30:04 +02:00 committed by GitHub
commit d54b666d02
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 101 additions and 27 deletions

View file

@ -47,6 +47,7 @@ import android.support.annotation.LayoutRes;
import android.support.annotation.NonNull;
import android.support.v4.content.res.ResourcesCompat;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatDelegate;
import android.view.Menu;
import android.view.MenuInflater;
@ -60,6 +61,7 @@ import com.owncloud.android.BuildConfig;
import com.owncloud.android.MainApp;
import com.owncloud.android.R;
import com.owncloud.android.authentication.AccountUtils;
import com.owncloud.android.authentication.PassCodeManager;
import com.owncloud.android.datamodel.ArbitraryDataProvider;
import com.owncloud.android.datamodel.ExternalLinksProvider;
import com.owncloud.android.datastorage.DataStorageProvider;
@ -71,6 +73,7 @@ import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
import com.owncloud.android.lib.common.utils.Log_OC;
import com.owncloud.android.utils.DeviceCredentialUtils;
import com.owncloud.android.utils.DisplayUtils;
import com.owncloud.android.utils.EncryptionUtils;
import com.owncloud.android.utils.MimeTypeUtil;
import com.owncloud.android.utils.ThemeUtils;
@ -119,6 +122,9 @@ public class Preferences extends PreferenceActivity
private String mStoragePath;
private String pendingLock;
private Account mAccount;
private ArbitraryDataProvider mArbitraryDataProvider;
public static class PreferenceKeys {
public static final String STORAGE_PATH = "storage_path";
public static final String INSTANT_UPLOAD_PATH = "instant_upload_path";
@ -150,6 +156,9 @@ public class Preferences extends PreferenceActivity
String appVersion = getAppVersion();
PreferenceScreen preferenceScreen = (PreferenceScreen) findPreference("preference_screen");
mAccount = AccountUtils.getCurrentOwnCloudAccount(getApplicationContext());
mArbitraryDataProvider = new ArbitraryDataProvider(getContentResolver());
// General
setupGeneralCategory(accentColor);
@ -307,6 +316,8 @@ public class Preferences extends PreferenceActivity
setupContactsBackupPreference(preferenceCategoryMore);
setupE2EMnemonicPreference(preferenceCategoryMore);
setupHelpPreference(preferenceCategoryMore);
setupRecommendPreference(preferenceCategoryMore);
@ -437,6 +448,34 @@ public class Preferences extends PreferenceActivity
}
}
private void setupE2EMnemonicPreference(PreferenceCategory preferenceCategoryMore) {
String mnemonic = mArbitraryDataProvider.getValue(mAccount.name, EncryptionUtils.MNEMONIC);
Preference pMnemonic = findPreference("mnemonic");
if (pMnemonic != null) {
if (!mnemonic.isEmpty() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (DeviceCredentialUtils.areCredentialsAvailable(this)) {
pMnemonic.setOnPreferenceClickListener(new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
Intent i = new Intent(MainApp.getAppContext(), RequestCredentialsActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
Preferences.this.startActivityForResult(i, PassCodeManager.PASSCODE_ACTIVITY);
return true;
}
});
} else {
pMnemonic.setEnabled(false);
pMnemonic.setSummary(R.string.prefs_e2e_no_device_credentials);
}
} else {
preferenceCategoryMore.removePreference(pMnemonic);
}
}
}
private void setupHelpPreference(PreferenceCategory preferenceCategoryMore) {
boolean helpEnabled = getResources().getBoolean(R.bool.help_enabled);
Preference pHelp = findPreference("help");
@ -645,16 +684,15 @@ public class Preferences extends PreferenceActivity
} else {
// Upload on WiFi
final ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(getContentResolver());
final Account account = AccountUtils.getCurrentOwnCloudAccount(getApplicationContext());
final SwitchPreference pUploadOnWifiCheckbox = (SwitchPreference) findPreference("synced_folder_on_wifi");
pUploadOnWifiCheckbox.setChecked(
arbitraryDataProvider.getBooleanValue(account, SYNCED_FOLDER_LIGHT_UPLOAD_ON_WIFI));
arbitraryDataProvider.getBooleanValue(mAccount, SYNCED_FOLDER_LIGHT_UPLOAD_ON_WIFI));
pUploadOnWifiCheckbox.setOnPreferenceClickListener(new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
arbitraryDataProvider.storeOrUpdateKeyValue(account.name, SYNCED_FOLDER_LIGHT_UPLOAD_ON_WIFI,
arbitraryDataProvider.storeOrUpdateKeyValue(mAccount.name, SYNCED_FOLDER_LIGHT_UPLOAD_ON_WIFI,
String.valueOf(pUploadOnWifiCheckbox.isChecked()));
return true;
@ -800,8 +838,6 @@ public class Preferences extends PreferenceActivity
}
private void launchDavDroidLogin() {
Account account = AccountUtils.getCurrentOwnCloudAccount(getApplicationContext());
Intent davDroidLoginIntent = new Intent();
davDroidLoginIntent.setClassName("at.bitfire.davdroid", "at.bitfire.davdroid.ui.setup.LoginActivity");
if (getPackageManager().resolveActivity(davDroidLoginIntent, 0) != null) {
@ -809,7 +845,7 @@ public class Preferences extends PreferenceActivity
if (mUri != null) {
davDroidLoginIntent.putExtra("url", mUri.toString() + DAV_PATH);
}
davDroidLoginIntent.putExtra("username", AccountUtils.getAccountUsername(account.name));
davDroidLoginIntent.putExtra("username", AccountUtils.getAccountUsername(mAccount.name));
//loginIntent.putExtra("password", "...");
startActivityForResult(davDroidLoginIntent, ACTION_REQUEST_CODE_DAVDROID_SETUP);
} else {
@ -835,8 +871,7 @@ public class Preferences extends PreferenceActivity
Thread t = new Thread(new Runnable() {
public void run() {
try {
Account account = AccountUtils.getCurrentOwnCloudAccount(getApplicationContext());
OwnCloudAccount ocAccount = new OwnCloudAccount(account, MainApp.getAppContext());
OwnCloudAccount ocAccount = new OwnCloudAccount(mAccount, MainApp.getAppContext());
mUri = OwnCloudClientManagerFactory.getDefaultSingleton().
getClientFor(ocAccount, getApplicationContext()).getBaseUri();
} catch (Throwable t) {
@ -888,7 +923,7 @@ public class Preferences extends PreferenceActivity
} else if (requestCode == ACTION_CONFIRM_DEVICE_CREDENTIALS && resultCode == RESULT_OK &&
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
data.getIntExtra(RequestCredentialsActivity.KEY_CHECK_RESULT,
RequestCredentialsActivity.KEY_CHECK_RESULT_FALSE) ==
RequestCredentialsActivity.KEY_CHECK_RESULT_FALSE) ==
RequestCredentialsActivity.KEY_CHECK_RESULT_TRUE) {
mLock.setValue(LOCK_NONE);
mLock.setSummary(mLock.getEntry());
@ -896,6 +931,22 @@ public class Preferences extends PreferenceActivity
if (!pendingLock.equals(LOCK_NONE)) {
enableLock(pendingLock);
}
} else if (requestCode == PassCodeManager.PASSCODE_ACTIVITY && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
data.getIntExtra(RequestCredentialsActivity.KEY_CHECK_RESULT,
RequestCredentialsActivity.KEY_CHECK_RESULT_FALSE) ==
RequestCredentialsActivity.KEY_CHECK_RESULT_TRUE) {
ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(getContentResolver());
String mnemonic = arbitraryDataProvider.getValue(mAccount.name, EncryptionUtils.MNEMONIC);
int accentColor = ThemeUtils.primaryAccentColor(this);
AlertDialog.Builder builder = new AlertDialog.Builder(this, R.style.FallbackTheming_Dialog);
builder.setTitle(ThemeUtils.getColoredTitle(getString(R.string.prefs_e2e_mnemonic), accentColor));
builder.setMessage(mnemonic);
builder.setPositiveButton(ThemeUtils.getColoredTitle(getString(R.string.common_ok), accentColor),
(dialog, which) -> dialog.dismiss());
builder.show();
}
}

View file

@ -185,9 +185,10 @@ public class SetupEncryptionDialogFragment extends DialogFragment {
try {
String privateKey = task.get();
String mnemonic = passwordField.getText().toString().replaceAll("\\s", "")
.toLowerCase(Locale.ROOT);
String decryptedPrivateKey = EncryptionUtils.decryptPrivateKey(privateKey,
passwordField.getText().toString().replaceAll("\\s", "")
.toLowerCase(Locale.ROOT));
mnemonic);
arbitraryDataProvider.storeOrUpdateKeyValue(account.name,
EncryptionUtils.PRIVATE_KEY, decryptedPrivateKey);
@ -195,6 +196,9 @@ public class SetupEncryptionDialogFragment extends DialogFragment {
dialog.dismiss();
Log_OC.d(TAG, "Private key successfully decrypted and stored");
arbitraryDataProvider.storeOrUpdateKeyValue(account.name, EncryptionUtils.MNEMONIC,
mnemonic);
Intent intentExisting = new Intent();
intentExisting.putExtra(SUCCESS, true);
intentExisting.putExtra(ARG_POSITION, getArguments().getInt(ARG_POSITION));
@ -301,14 +305,7 @@ public class SetupEncryptionDialogFragment extends DialogFragment {
textView.setText(R.string.end_to_end_encryption_keywords_description);
StringBuilder stringBuilder = new StringBuilder();
for (String string : keyWords) {
stringBuilder.append(string).append(" ");
}
String keys = stringBuilder.toString();
passphraseTextView.setText(keys);
passphraseTextView.setText(generateMnemonicString(true));
passphraseTextView.setVisibility(View.VISIBLE);
positiveButton.setText(R.string.end_to_end_encryption_confirm_button);
@ -379,15 +376,10 @@ public class SetupEncryptionDialogFragment extends DialogFragment {
return "";
}
StringBuilder stringBuilder = new StringBuilder();
for (String string : keyWords) {
stringBuilder.append(string);
}
String keyPhrase = stringBuilder.toString();
String privateKeyString = EncryptionUtils.encodeBytesToBase64String(privateKey.getEncoded());
String privatePemKeyString = EncryptionUtils.privateKeyToPEM(privateKey);
String encryptedPrivateKey = EncryptionUtils.encryptPrivateKey(privatePemKeyString, keyPhrase);
String encryptedPrivateKey = EncryptionUtils.encryptPrivateKey(privatePemKeyString,
generateMnemonicString(false));
// upload encryptedPrivateKey
StorePrivateKeyOperation storePrivateKeyOperation = new StorePrivateKeyOperation(encryptedPrivateKey);
@ -400,6 +392,8 @@ public class SetupEncryptionDialogFragment extends DialogFragment {
arbitraryDataProvider.storeOrUpdateKeyValue(account.name, EncryptionUtils.PRIVATE_KEY,
privateKeyString);
arbitraryDataProvider.storeOrUpdateKeyValue(account.name, EncryptionUtils.PUBLIC_KEY, publicKey);
arbitraryDataProvider.storeOrUpdateKeyValue(account.name, EncryptionUtils.MNEMONIC,
generateMnemonicString(true));
keyResult = KEY_CREATED;
return (String) storePrivateKeyResult.getData().get(0);
@ -438,4 +432,17 @@ public class SetupEncryptionDialogFragment extends DialogFragment {
}
}
}
private String generateMnemonicString(boolean withWhitespace) {
StringBuilder stringBuilder = new StringBuilder();
for (String string : keyWords) {
stringBuilder.append(string);
if (withWhitespace) {
stringBuilder.append(' ');
}
}
return stringBuilder.toString();
}
}

View file

@ -95,6 +95,7 @@ public class EncryptionUtils {
public static final String PUBLIC_KEY = "PUBLIC_KEY";
public static final String PRIVATE_KEY = "PRIVATE_KEY";
public static final String MNEMONIC = "MNEMONIC";
public static final int ivLength = 16;
public static final int saltLength = 40;

View file

@ -796,7 +796,9 @@
<string name="sharee_add_failed">Adding sharee failed</string>
<string name="unsharing_failed">Unsharing failed</string>
<string name="updating_share_failed">Updating share failed</string>
<string name="whats_new_device_credentials_title">Use Android device protection</string>
<string name="prefs_e2e_mnemonic">E2E mnemonic</string>
<string name="prefs_e2e_no_device_credentials">To show mnemonic please enable device credentials.</string>
<string name="whats_new_device_credentials_title">Use Android\'s device internal protection</string>
<string name="whats_new_device_credentials_content">Use anything like a pattern, password, pin or your fingerprint to keep your data safe.</string>
<string name="restore_button_description">Restore deleted file</string>
<string name="restore">Restore file</string>

View file

@ -45,6 +45,15 @@
<item name="colorAccent">#757575</item>
</style>
<style name="FallbackTheming.Dialog" parent="Theme.AppCompat.Light.Dialog.Alert">
<item name="colorPrimary">#424242</item>
<item name="colorPrimaryDark">#212121</item>
<item name="colorAccent">#757575</item>
<item name="windowNoTitle">false</item>
<item name="buttonBarButtonStyle">@style/Theme.ownCloud.Dialog.ButtonBar.Button</item>
<item name="buttonBarStyle">@style/Theme.ownCloud.Dialog.ButtonBar</item>
</style>
<!-- seperate action bar style for activities without an action bar -->
<style name="Theme.ownCloud.Toolbar" parent="Theme.AppCompat.Light.NoActionBar">
<item name="windowNoTitle">true</item>

View file

@ -60,6 +60,10 @@
android:title="@string/actionbar_contacts"
android:key="contacts"
android:summary="@string/prefs_daily_contacts_sync_summary"/>
<Preference
android:title="@string/prefs_e2e_mnemonic"
android:key="mnemonic"
android:summary="Displays your E2E 12 words passphrase" />
<Preference android:title="@string/prefs_help" android:key="help" />
<Preference android:title="@string/prefs_recommend" android:key="recommend" />
<Preference android:title="@string/prefs_feedback" android:key="feedback" />