diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 85836024..3a3a790b 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -236,6 +236,7 @@
+
diff --git a/app/src/main/java/com/etesync/syncadapter/journalmanager/UserInfoManager.kt b/app/src/main/java/com/etesync/syncadapter/journalmanager/UserInfoManager.kt
index 055661f8..544597c4 100644
--- a/app/src/main/java/com/etesync/syncadapter/journalmanager/UserInfoManager.kt
+++ b/app/src/main/java/com/etesync/syncadapter/journalmanager/UserInfoManager.kt
@@ -83,6 +83,8 @@ class UserInfoManager(httpClient: OkHttpClient, remote: HttpUrl) : BaseManager()
class UserInfo {
@Transient
var owner: String? = null
+ @Transient
+ val plan: String? = null
val version: Byte?
val pubkey: ByteArray?
private var content: ByteArray? = null
diff --git a/app/src/main/java/com/etesync/syncadapter/ui/AccountSettingsActivity.kt b/app/src/main/java/com/etesync/syncadapter/ui/AccountSettingsActivity.kt
index b7d1b001..91e83806 100644
--- a/app/src/main/java/com/etesync/syncadapter/ui/AccountSettingsActivity.kt
+++ b/app/src/main/java/com/etesync/syncadapter/ui/AccountSettingsActivity.kt
@@ -95,6 +95,13 @@ class AccountSettingsActivity : BaseActivity() {
false
}
+ // Category: encryption
+ val prefEncryptionPassword = findPreference("encryption_password")
+ prefEncryptionPassword.onPreferenceClickListener = Preference.OnPreferenceClickListener { _ ->
+ startActivity(ChangeEncryptionPasswordActivity.newIntent(activity!!, account))
+ true
+ }
+
// category: synchronization
val prefSyncContacts = findPreference("sync_interval_contacts") as ListPreference
val syncIntervalContacts = settings.getSyncInterval(App.addressBooksAuthority)
diff --git a/app/src/main/java/com/etesync/syncadapter/ui/ChangeEncryptionPasswordActivity.kt b/app/src/main/java/com/etesync/syncadapter/ui/ChangeEncryptionPasswordActivity.kt
new file mode 100644
index 00000000..08e604ed
--- /dev/null
+++ b/app/src/main/java/com/etesync/syncadapter/ui/ChangeEncryptionPasswordActivity.kt
@@ -0,0 +1,194 @@
+/*
+ * Copyright © 2013 – 2016 Ricki Hirner (bitfire web engineering).
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the GNU Public License v3.0
+ * which accompanies this distribution, and is available at
+ * http://www.gnu.org/licenses/gpl.html
+ */
+
+package com.etesync.syncadapter.ui
+
+import android.accounts.Account
+import android.app.ProgressDialog
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import android.support.design.widget.TextInputLayout
+import android.support.v7.app.AlertDialog
+import android.view.View
+import com.etesync.syncadapter.AccountSettings
+import com.etesync.syncadapter.App
+import com.etesync.syncadapter.HttpClient
+import com.etesync.syncadapter.R
+import com.etesync.syncadapter.journalmanager.Crypto
+import com.etesync.syncadapter.journalmanager.JournalManager
+import com.etesync.syncadapter.journalmanager.UserInfoManager
+import okhttp3.HttpUrl
+import org.jetbrains.anko.doAsync
+import org.jetbrains.anko.uiThread
+import java.util.*
+
+open class ChangeEncryptionPasswordActivity : BaseActivity() {
+
+ protected lateinit var account: Account
+ lateinit var progress: ProgressDialog
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ account = intent.extras!!.getParcelable(EXTRA_ACCOUNT)!!
+
+ supportActionBar!!.setDisplayHomeAsUpEnabled(true)
+
+ setContentView(R.layout.change_encryption_password)
+ }
+
+ fun onCancelClicked(v: View) {
+ finish()
+ }
+
+ fun changePasswordError(e: Exception) {
+ progress.dismiss()
+ AlertDialog.Builder(this)
+ .setTitle(R.string.wrong_encryption_password)
+ .setIcon(R.drawable.ic_error_dark)
+ .setMessage(getString(R.string.wrong_encryption_password_content, e.localizedMessage))
+ .setPositiveButton(android.R.string.ok) { _, _ ->
+ // dismiss
+ }.show()
+ }
+
+ fun changePasswordDo(old_password: String, new_password: String) {
+ val settings = AccountSettings(this, account)
+ val httpClient = HttpClient.create(this, settings)
+
+ doAsync {
+ App.log.info("Started deriving old key")
+ val old_key = Crypto.deriveKey(account.name, old_password)
+ App.log.info("Finished deriving old key")
+
+ var cryptoManager: Crypto.CryptoManager
+ val principal = HttpUrl.get(settings.uri!!)!!
+
+ try {
+ val userInfoManager = UserInfoManager(httpClient, principal)
+ val userInfo = userInfoManager.fetch(account.name)!!
+ App.log.info("Fetched userInfo for " + account.name)
+ cryptoManager = Crypto.CryptoManager(userInfo.version!!.toInt(), old_key, "userInfo")
+ userInfo.verify(cryptoManager)
+
+ App.log.info("Started deriving new key")
+ val new_key = Crypto.deriveKey(account.name, new_password)
+ App.log.info("Finished deriving new key")
+
+ val userInfoContent = userInfo.getContent(cryptoManager)!!
+ cryptoManager = Crypto.CryptoManager(userInfo.version.toInt(), new_key, "userInfo")
+ userInfo.setContent(cryptoManager, userInfoContent)
+
+ App.log.info("Fetching journal list")
+ val membersToAdd = LinkedList>()
+ val journalManager = JournalManager(httpClient, principal)
+ val journals = journalManager.list()
+ for (journal in journals) {
+ if (journal.owner != account.name) {
+ continue
+ }
+
+ if (journal.key != null) {
+ // We don't need to handle those cases, as they are already encrypted using pubkey
+ continue
+ } else {
+ cryptoManager = Crypto.CryptoManager(journal.version, old_key, journal.uid!!)
+ }
+
+ App.log.info("Converting journal ${journal.uid}")
+ journal.verify(cryptoManager)
+
+ membersToAdd.add(Pair(journal, cryptoManager.getEncryptedKey(settings.keyPair!!, userInfo.pubkey!!)))
+ }
+
+ App.log.info("Finished converting account. Uploading changes")
+ userInfoManager.update(userInfo)
+
+ for ((journal, encryptedKey) in membersToAdd) {
+ if (journal.owner != account.name) {
+ continue
+ }
+
+ App.log.info("Uploading journal ${journal.uid}")
+ val member = JournalManager.Member(account.name, encryptedKey!!)
+ journalManager.addMember(journal, member)
+ }
+
+ settings.password(new_key)
+ App.log.info("Finished uploading changes. Encryption password changed successfully.")
+
+ uiThread {
+ progress.dismiss()
+ AlertDialog.Builder(this@ChangeEncryptionPasswordActivity)
+ .setTitle(R.string.change_encryption_password_success_title)
+ .setMessage(R.string.change_encryption_password_success_body)
+ .setPositiveButton(android.R.string.ok) { _, _ ->
+ this@ChangeEncryptionPasswordActivity.finish()
+ }.show()
+ }
+ } catch (e: Exception) {
+ uiThread {
+ changePasswordError(e)
+ }
+ return@doAsync
+ }
+ }
+ }
+
+ fun changePasswordClicked(v: View) {
+ val old_password_view = findViewById(R.id.encryption_password)
+ val new_password_view = findViewById(R.id.new_encryption_password)
+
+ var valid = true
+ val old_password = old_password_view.editText?.text.toString()
+ if (old_password.isEmpty()) {
+ old_password_view.error = getString(R.string.login_password_required)
+ valid = false
+ } else {
+ old_password_view.error = null
+ }
+ val new_password = new_password_view.editText?.text.toString()
+ if (new_password.isEmpty()) {
+ new_password_view.error = getString(R.string.login_password_required)
+ valid = false
+ } else {
+ new_password_view.error = null
+ }
+
+ if (!valid) {
+ return
+ }
+
+ AlertDialog.Builder(this)
+ .setTitle(R.string.delete_collection_confirm_title)
+ .setMessage(R.string.change_encryption_password_are_you_sure)
+ .setPositiveButton(android.R.string.yes) { _, _ ->
+ changePasswordDo(old_password, new_password)
+ progress = ProgressDialog(this)
+ progress.setTitle(R.string.login_encryption_setup_title)
+ progress.setMessage(getString(R.string.login_encryption_setup))
+ progress.isIndeterminate = true
+ progress.setCanceledOnTouchOutside(false)
+ progress.setCancelable(false)
+ progress.show()
+ }
+ .setNegativeButton(android.R.string.no) { _, _ -> }
+ .create().show()
+ }
+
+ companion object {
+ internal val EXTRA_ACCOUNT = "account"
+
+ fun newIntent(context: Context, account: Account): Intent {
+ val intent = Intent(context, ChangeEncryptionPasswordActivity::class.java)
+ intent.putExtra(CreateCollectionActivity.EXTRA_ACCOUNT, account)
+ return intent
+ }
+ }
+}
diff --git a/app/src/main/res/layout/change_encryption_password.xml b/app/src/main/res/layout/change_encryption_password.xml
new file mode 100644
index 00000000..bb853a35
--- /dev/null
+++ b/app/src/main/res/layout/change_encryption_password.xml
@@ -0,0 +1,104 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 7f515334..b4e72da0 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -126,7 +126,7 @@
Ihr Anmelde-Passwort
Passwort eingeben:
Verschlüsselungs-Passwort
- Verschlüsselungs-Passwort ändern (nicht implementiert)
+ Verschlüsselungs-Passwort ändern
Verschlüsselungs-Passwort eingeben:
Synchronisation
Häufigkeit der Kontakte-Synchronisation
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index 9f462b38..4bda075e 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -205,7 +205,7 @@
Zmień hasło weryfikacji
Wprowadź obecne hasło:
Hasło szyfrowania
- Zmień swoje hasło szyfrowania (niezaimpletowane)
+ Zmień swoje hasło szyfrowania
Wprowadź obecne hasło szyfrowania:
Synchronizacja
Okres synchronizacji kontaktów
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index d8f38515..9fd28f61 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -184,7 +184,7 @@
Invalid URL found, did you forget to include https://?
Show advanced settings
Encryption Password
- * Please double-check the password, as it can\'t be changed if wrong.
+ * Please double-check the password, as it can\'t be recovered if wrong!
This password is used to encrypt your data, unlike the previous one, which is used to log into the service.\nYou are asked to choose a separate encryption password for security reasons. For more information, plesae refer to the FAQ at: %s
Password required
Log In
@@ -207,12 +207,19 @@
Account creation failed
Wrong encryption password
- Got an integrity error while accessing your account, which most likely means you put in the wrong encryption password.\n\n%s
+ Got an integrity error while accessing your account, which most likely means you put in the wrong encryption password.\n\nError: %s
+
+
+ Change Encryption Password
+ Please don\'t use this tool if you believe your encryption password has been comporomised. Contact support instead.
+ New encryption Password
+ Are you sure you would like to continue? The process can not be stopped half way or be undone, and may take a while to complete.
+ Encryption Password Change
+ Encryption password has been successfully changed!
Error Setting User Info
-
Import
Import Failed
@@ -230,7 +237,7 @@
Change your authentication password
Enter your password:
Encryption Password
- Change your encryption password (not implemented)
+ Change your encryption password
Enter your encryption password:
Synchronization
Contacts sync. interval
diff --git a/app/src/main/res/xml/settings_account.xml b/app/src/main/res/xml/settings_account.xml
index bc1c2047..cabeece8 100644
--- a/app/src/main/res/xml/settings_account.xml
+++ b/app/src/main/res/xml/settings_account.xml
@@ -23,14 +23,12 @@
-
+ android:persistent="false"
+ />