diff options
author | pacien | 2017-09-07 14:27:37 +0200 |
---|---|---|
committer | pacien | 2017-09-07 14:27:37 +0200 |
commit | 4989dad67b68b38e75416916df406dcec908b399 (patch) | |
tree | 1857d313393788234ffa75b9203122d3ae822d3f /app/src/main/java/org | |
parent | 9e5aa1a9f95a3b5541aba7e511e13ae51b614416 (diff) | |
download | tincapp-4989dad67b68b38e75416916df406dcec908b399.tar.gz |
Implement encrypted private keys support
Diffstat (limited to 'app/src/main/java/org')
9 files changed, 234 insertions, 24 deletions
diff --git a/app/src/main/java/org/pacien/tincapp/activities/ConfigureActivity.kt b/app/src/main/java/org/pacien/tincapp/activities/ConfigureActivity.kt index 9027b14..6c29a53 100644 --- a/app/src/main/java/org/pacien/tincapp/activities/ConfigureActivity.kt +++ b/app/src/main/java/org/pacien/tincapp/activities/ConfigureActivity.kt | |||
@@ -10,6 +10,7 @@ import android.support.v7.app.AlertDialog | |||
10 | import android.view.View | 10 | import android.view.View |
11 | import java8.util.concurrent.CompletableFuture | 11 | import java8.util.concurrent.CompletableFuture |
12 | import kotlinx.android.synthetic.main.base.* | 12 | import kotlinx.android.synthetic.main.base.* |
13 | import kotlinx.android.synthetic.main.dialog_encrypt_decrypt_keys.view.* | ||
13 | import kotlinx.android.synthetic.main.dialog_network_generate.view.* | 14 | import kotlinx.android.synthetic.main.dialog_network_generate.view.* |
14 | import kotlinx.android.synthetic.main.dialog_network_join.view.* | 15 | import kotlinx.android.synthetic.main.dialog_network_join.view.* |
15 | import kotlinx.android.synthetic.main.page_configure.* | 16 | import kotlinx.android.synthetic.main.page_configure.* |
@@ -62,7 +63,10 @@ class ConfigureActivity : BaseActivity() { | |||
62 | val genDialog = layoutInflater.inflate(R.layout.dialog_network_generate, main_content, false) | 63 | val genDialog = layoutInflater.inflate(R.layout.dialog_network_generate, main_content, false) |
63 | AlertDialog.Builder(this).setTitle(R.string.title_new_network).setView(genDialog) | 64 | AlertDialog.Builder(this).setTitle(R.string.title_new_network).setView(genDialog) |
64 | .setPositiveButton(R.string.action_create) { _, _ -> | 65 | .setPositiveButton(R.string.action_create) { _, _ -> |
65 | generateConf(genDialog.new_net_name.text.toString(), genDialog.new_node_name.text.toString()) | 66 | generateConf( |
67 | genDialog.new_net_name.text.toString(), | ||
68 | genDialog.new_node_name.text.toString(), | ||
69 | genDialog.new_passphrase.text.toString()) | ||
66 | }.setNegativeButton(R.string.action_cancel, App.dismissAction).show() | 70 | }.setNegativeButton(R.string.action_cancel, App.dismissAction).show() |
67 | } | 71 | } |
68 | 72 | ||
@@ -70,7 +74,21 @@ class ConfigureActivity : BaseActivity() { | |||
70 | joinDialog = layoutInflater.inflate(R.layout.dialog_network_join, main_content, false) | 74 | joinDialog = layoutInflater.inflate(R.layout.dialog_network_join, main_content, false) |
71 | AlertDialog.Builder(this).setTitle(R.string.title_join_network).setView(joinDialog) | 75 | AlertDialog.Builder(this).setTitle(R.string.title_join_network).setView(joinDialog) |
72 | .setPositiveButton(R.string.action_join) { _, _ -> | 76 | .setPositiveButton(R.string.action_join) { _, _ -> |
73 | joinNetwork(joinDialog!!.net_name.text.toString(), joinDialog!!.invitation_url.text.toString()) | 77 | joinNetwork( |
78 | joinDialog!!.net_name.text.toString(), | ||
79 | joinDialog!!.invitation_url.text.toString(), | ||
80 | joinDialog!!.join_passphrase.text.toString()) | ||
81 | }.setNegativeButton(R.string.action_cancel, App.dismissAction).show() | ||
82 | } | ||
83 | |||
84 | fun openEncryptDecryptPrivateKeyDialog(@Suppress("UNUSED_PARAMETER") v: View) { | ||
85 | val encryptDecryptDialog = layoutInflater.inflate(R.layout.dialog_encrypt_decrypt_keys, main_content, false) | ||
86 | AlertDialog.Builder(this).setTitle(R.string.title_private_keys_encryption).setView(encryptDecryptDialog) | ||
87 | .setPositiveButton(R.string.action_apply) { _, _ -> | ||
88 | encryptDecryptPrivateKeys( | ||
89 | encryptDecryptDialog!!.enc_dec_net_name.text.toString(), | ||
90 | encryptDecryptDialog.enc_dec_current_passphrase.text.toString(), | ||
91 | encryptDecryptDialog.enc_dec_new_passphrase.text.toString()) | ||
74 | }.setNegativeButton(R.string.action_cancel, App.dismissAction).show() | 92 | }.setNegativeButton(R.string.action_cancel, App.dismissAction).show() |
75 | } | 93 | } |
76 | 94 | ||
@@ -80,22 +98,28 @@ class ConfigureActivity : BaseActivity() { | |||
80 | text_tinc_binary.text = AppPaths.tinc().absolutePath | 98 | text_tinc_binary.text = AppPaths.tinc().absolutePath |
81 | } | 99 | } |
82 | 100 | ||
83 | private fun generateConf(netName: String, nodeName: String) = execAction( | 101 | private fun generateConf(netName: String, nodeName: String, passphrase: String? = null) = execAction( |
84 | R.string.message_generating_configuration, | 102 | R.string.message_generating_configuration, |
85 | Tinc.init(netName, nodeName) | 103 | Tinc.init(netName, nodeName) |
86 | .thenCompose { TincApp.removeScripts(netName) }) | 104 | .thenCompose { TincApp.removeScripts(netName) } |
105 | .thenCompose { TincApp.setPassphrase(netName, newPassphrase = passphrase) }) | ||
87 | 106 | ||
88 | private fun joinNetwork(netName: String, url: String) = execAction( | 107 | private fun joinNetwork(netName: String, url: String, passphrase: String? = null) = execAction( |
89 | R.string.message_joining_network, | 108 | R.string.message_joining_network, |
90 | Tinc.join(netName, url) | 109 | Tinc.join(netName, url) |
91 | .thenCompose { TincApp.removeScripts(netName) } | 110 | .thenCompose { TincApp.removeScripts(netName) } |
92 | .thenCompose { TincApp.generateIfaceCfg(netName) }) | 111 | .thenCompose { TincApp.generateIfaceCfg(netName) } |
112 | .thenCompose { TincApp.setPassphrase(netName, newPassphrase = passphrase) }) | ||
113 | |||
114 | private fun encryptDecryptPrivateKeys(netName: String, currentPassphrase: String, newPassphrase: String) = execAction( | ||
115 | R.string.message_encrypting_decrypting_private_keys, | ||
116 | TincApp.setPassphrase(netName, currentPassphrase, newPassphrase)) | ||
93 | 117 | ||
94 | private fun execAction(@StringRes label: Int, action: CompletableFuture<Void>) { | 118 | private fun execAction(@StringRes label: Int, action: CompletableFuture<Void>) { |
95 | showProgressDialog(label).let { progressDialog -> | 119 | showProgressDialog(label).let { progressDialog -> |
96 | action | 120 | action |
97 | .whenComplete { _, _ -> progressDialog.dismiss() } | 121 | .whenComplete { _, _ -> progressDialog.dismiss() } |
98 | .thenAccept { notify(R.string.message_network_configuration_created) } | 122 | .thenAccept { notify(R.string.message_network_configuration_written) } |
99 | .exceptionallyAccept { runOnUiThread { showErrorDialog(it.cause!!.localizedMessage) } } | 123 | .exceptionallyAccept { runOnUiThread { showErrorDialog(it.cause!!.localizedMessage) } } |
100 | } | 124 | } |
101 | } | 125 | } |
diff --git a/app/src/main/java/org/pacien/tincapp/activities/LaunchActivity.kt b/app/src/main/java/org/pacien/tincapp/activities/LaunchActivity.kt index 6eb630d..74a059b 100644 --- a/app/src/main/java/org/pacien/tincapp/activities/LaunchActivity.kt +++ b/app/src/main/java/org/pacien/tincapp/activities/LaunchActivity.kt | |||
@@ -1,20 +1,28 @@ | |||
1 | package org.pacien.tincapp.activities | 1 | package org.pacien.tincapp.activities |
2 | 2 | ||
3 | import android.annotation.SuppressLint | ||
3 | import android.app.Activity | 4 | import android.app.Activity |
4 | import android.content.Intent | 5 | import android.content.Intent |
5 | import android.net.Uri | 6 | import android.net.Uri |
6 | import android.net.VpnService | 7 | import android.net.VpnService |
7 | import android.os.Bundle | 8 | import android.os.Bundle |
9 | import android.support.v7.app.AlertDialog | ||
10 | import android.support.v7.app.AppCompatActivity | ||
11 | import kotlinx.android.synthetic.main.dialog_decrypt_keys.view.* | ||
12 | import org.pacien.tincapp.R | ||
13 | import org.pacien.tincapp.commands.TincApp | ||
8 | import org.pacien.tincapp.context.App | 14 | import org.pacien.tincapp.context.App |
9 | import org.pacien.tincapp.intent.action.ACTION_CONNECT | 15 | import org.pacien.tincapp.intent.action.ACTION_CONNECT |
10 | import org.pacien.tincapp.intent.action.ACTION_DISCONNECT | 16 | import org.pacien.tincapp.intent.action.ACTION_DISCONNECT |
11 | import org.pacien.tincapp.intent.action.TINC_SCHEME | 17 | import org.pacien.tincapp.intent.action.TINC_SCHEME |
12 | import org.pacien.tincapp.service.TincVpnService | 18 | import org.pacien.tincapp.service.TincVpnService |
19 | import org.pacien.tincapp.utils.PemUtils | ||
20 | import java.io.FileNotFoundException | ||
13 | 21 | ||
14 | /** | 22 | /** |
15 | * @author pacien | 23 | * @author pacien |
16 | */ | 24 | */ |
17 | class LaunchActivity : Activity() { | 25 | class LaunchActivity : AppCompatActivity() { |
18 | 26 | ||
19 | override fun onCreate(savedInstanceState: Bundle?) { | 27 | override fun onCreate(savedInstanceState: Bundle?) { |
20 | super.onCreate(savedInstanceState) | 28 | super.onCreate(savedInstanceState) |
@@ -26,15 +34,41 @@ class LaunchActivity : Activity() { | |||
26 | } | 34 | } |
27 | 35 | ||
28 | override fun onActivityResult(request: Int, result: Int, data: Intent?) { | 36 | override fun onActivityResult(request: Int, result: Int, data: Intent?) { |
29 | if (result == Activity.RESULT_OK) TincVpnService.startVpn(intent.data.schemeSpecificPart) | 37 | if (request == PERMISSION_REQUEST_CODE && result == Activity.RESULT_OK) askPassphrase() |
30 | finish() | ||
31 | } | 38 | } |
32 | 39 | ||
33 | private fun requestPerm() = VpnService.prepare(this).let { | 40 | private fun requestPerm() = VpnService.prepare(this).let { |
34 | if (it != null) | 41 | if (it != null) |
35 | startActivityForResult(it, 0) | 42 | startActivityForResult(it, PERMISSION_REQUEST_CODE) |
36 | else | 43 | else |
37 | onActivityResult(0, Activity.RESULT_OK, null) | 44 | onActivityResult(PERMISSION_REQUEST_CODE, Activity.RESULT_OK, null) |
45 | } | ||
46 | |||
47 | @SuppressLint("InflateParams") | ||
48 | private fun askPassphrase() { | ||
49 | val netName = intent.data.schemeSpecificPart | ||
50 | |||
51 | if (needPassphrase(netName) && intent.data.fragment == null) { | ||
52 | val dialog = layoutInflater.inflate(R.layout.dialog_decrypt_keys, null, false) | ||
53 | AlertDialog.Builder(this) | ||
54 | .setTitle(R.string.title_unlock_private_keys).setView(dialog) | ||
55 | .setPositiveButton(R.string.action_unlock) { _, _ -> connect(netName, dialog.passphrase.text.toString()) } | ||
56 | .setNegativeButton(R.string.action_cancel, { _, _ -> finish() }) | ||
57 | .show() | ||
58 | } else { | ||
59 | connect(netName, intent.data.fragment) | ||
60 | } | ||
61 | } | ||
62 | |||
63 | private fun needPassphrase(netName: String) = try { | ||
64 | TincApp.listPrivateKeys(netName).filter { it.exists() }.any { PemUtils.isEncrypted(PemUtils.read(it)) } | ||
65 | } catch (e: FileNotFoundException) { | ||
66 | false | ||
67 | } | ||