Skip to content
This repository was archived by the owner on Oct 15, 2024. It is now read-only.

Commit a2a9386

Browse files
Skrilltraxmsfjarvis
authored andcommitted
wip: decrypt files using key imported during onboarding
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
1 parent 6326742 commit a2a9386

File tree

5 files changed

+82
-36
lines changed

5 files changed

+82
-36
lines changed

app/src/main/java/dev/msfjarvis/aps/ui/crypto/GopenpgpDecryptActivity.kt

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import android.view.MenuItem
1212
import androidx.lifecycle.lifecycleScope
1313
import dagger.hilt.android.AndroidEntryPoint
1414
import dev.msfjarvis.aps.R
15+
import dev.msfjarvis.aps.data.crypto.AndroidKeyManager
1516
import dev.msfjarvis.aps.data.crypto.GopenpgpCryptoHandler
1617
import dev.msfjarvis.aps.data.passfile.PasswordEntry
1718
import dev.msfjarvis.aps.data.password.FieldItem
@@ -36,6 +37,7 @@ class GopenpgpDecryptActivity : BasePgpActivity() {
3637
private val binding by viewBinding(DecryptLayoutBinding::inflate)
3738
@Inject lateinit var passwordEntryFactory: PasswordEntryFactory
3839
@Inject lateinit var gopenpgpCrypto: GopenpgpCryptoHandler
40+
@Inject lateinit var gpgKeyManager: AndroidKeyManager
3941
private val relativeParentPath by unsafeLazy { getParentPath(fullPath, repoPath) }
4042

4143
private var passwordEntry: PasswordEntry? = null
@@ -125,12 +127,13 @@ class GopenpgpDecryptActivity : BasePgpActivity() {
125127
private fun decrypt() =
126128
lifecycleScope.launch {
127129
// TODO(msfjarvis): native methods are fallible, add error handling once out of testing
128-
val message = withContext(Dispatchers.IO) { File(fullPath).readBytes() }
130+
val message = withContext(Dispatchers.IO) { File(fullPath).readText() }
129131
val result =
130132
withContext(Dispatchers.IO) {
131-
gopenpgpCrypto.decrypt(
132-
PRIV_KEY,
133-
PASS.toByteArray(charset = Charsets.UTF_8),
133+
// TODO(aditya): Use .gpg-id to get the key id
134+
gopenpgpCrypto.decryptFromKeyId(
135+
"b950ae2813841585",
136+
"12345678".toByteArray(charset = Charsets.UTF_8),
134137
message,
135138
)
136139
}

app/src/main/java/dev/msfjarvis/aps/ui/onboarding/fragments/GopenpgpKeySelectionFragment.kt

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import androidx.activity.result.contract.ActivityResultContracts
1212
import androidx.core.content.edit
1313
import androidx.fragment.app.Fragment
1414
import androidx.fragment.app.viewModels
15+
import androidx.lifecycle.lifecycleScope
16+
import com.github.ajalt.timberkt.d
1517
import com.github.michaelbull.result.onFailure
1618
import com.github.michaelbull.result.onSuccess
1719
import dagger.hilt.android.AndroidEntryPoint
@@ -24,6 +26,7 @@ import dev.msfjarvis.aps.util.extensions.sharedPrefs
2426
import dev.msfjarvis.aps.util.extensions.unsafeLazy
2527
import dev.msfjarvis.aps.util.extensions.viewBinding
2628
import dev.msfjarvis.aps.util.settings.PreferenceKeys
29+
import kotlinx.coroutines.flow.launchIn
2730
import kotlinx.coroutines.flow.onEach
2831

2932
@AndroidEntryPoint
@@ -32,19 +35,18 @@ class GopenpgpKeySelectionFragment : Fragment(R.layout.fragment_key_selection) {
3235
private val settings by unsafeLazy { requireActivity().applicationContext.sharedPrefs }
3336
private val binding by viewBinding(FragmentKeySelectionBinding::bind)
3437
private val viewModel: KeySelectionViewModel by viewModels()
35-
36-
private val gpgKeySelectAction = registerForActivityResult(ActivityResultContracts.GetContent()) { uri ->
37-
if (uri == null) {
38-
// TODO: Use string resources here
39-
showError("No files chosen")
40-
return@registerForActivityResult
41-
}
42-
43-
val fis = requireContext().contentResolver.openInputStream(uri)
44-
if (fis == null) {
45-
showError("Error resolving content uri")
46-
return@registerForActivityResult
47-
}
38+
private val gpgKeySelectAction =
39+
registerForActivityResult(ActivityResultContracts.GetContent()) { uri ->
40+
if (uri == null) {
41+
// TODO: Use string resources here
42+
showError("No files chosen")
43+
return@registerForActivityResult
44+
}
45+
val fis = requireContext().contentResolver.openInputStream(uri)
46+
if (fis == null) {
47+
showError("Error resolving content uri")
48+
return@registerForActivityResult
49+
}
4850

4951
viewModel.importKey(fis)
5052
}
@@ -53,15 +55,24 @@ class GopenpgpKeySelectionFragment : Fragment(R.layout.fragment_key_selection) {
5355
super.onViewCreated(view, savedInstanceState)
5456
binding.selectKey.setOnClickListener { gpgKeySelectAction.launch("*/*") }
5557

56-
viewModel.importKeyStatus.onEach { result ->
57-
result.onSuccess {
58-
settings.edit { putBoolean(PreferenceKeys.REPOSITORY_INITIALIZED, true) }
59-
requireActivity().commitChange(getString(R.string.git_commit_gpg_id, getString(R.string.app_name)))
60-
finish()
61-
}.onFailure {
62-
showError(it.message!!)
58+
// TODO: Use new flowWithLifecycle APIs
59+
viewModel
60+
.importKeyStatus
61+
.onEach { result ->
62+
result
63+
.onSuccess {
64+
d { "Key imported successfully" }
65+
settings.edit { putBoolean(PreferenceKeys.REPOSITORY_INITIALIZED, true) }
66+
requireActivity()
67+
.commitChange(getString(R.string.git_commit_gpg_id, getString(R.string.app_name)))
68+
finish()
69+
}
70+
.onFailure {
71+
d(it)
72+
showError(it.message!!)
73+
}
6374
}
64-
}
75+
.launchIn(viewLifecycleOwner.lifecycleScope)
6576
}
6677

6778
private fun showError(message: String) {

app/src/main/java/dev/msfjarvis/aps/ui/onboarding/viewmodel/KeySelectionViewModel.kt

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package dev.msfjarvis.aps.ui.onboarding.viewmodel
22

3+
import android.util.Log
34
import androidx.lifecycle.ViewModel
45
import androidx.lifecycle.viewModelScope
56
import com.github.michaelbull.result.Err
@@ -61,9 +62,13 @@ class KeySelectionViewModel @Inject constructor(private val keyManager: KeyManag
6162
Regex("END .* PRIVATE KEY").containsMatchIn(lines.last()))
6263
}
6364

64-
private suspend fun createGpgIdFile(): Result<Unit, Throwable> = withContext(Dispatchers.IO) {
65-
return@withContext keyManager.listKeyIds().map { keys ->
66-
val idFile = File(PasswordRepository.getRepositoryDirectory(), ".gpg-id")
67-
idFile.writeText((keys + "").joinToString("\n"))
65+
private suspend fun createGpgIdFile(): Result<Unit, Throwable> =
66+
withContext(Dispatchers.IO) {
67+
return@withContext keyManager.listKeyIds().map { keys ->
68+
Log.d("CreateIdFile", "$keys")
69+
val idFile = File(PasswordRepository.getRepositoryDirectory(), ".gpg-id")
70+
idFile.createNewFile()
71+
idFile.writeText((keys + "").joinToString("\n"))
72+
}
6873
}
6974
}

crypto-pgp/src/main/kotlin/dev/msfjarvis/aps/data/crypto/AndroidKeyManager.kt

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package dev.msfjarvis.aps.data.crypto
33
import com.github.michaelbull.result.Err
44
import com.github.michaelbull.result.Result
55
import com.github.michaelbull.result.runCatching
6+
import com.proton.Gopenpgp.crypto.Crypto
67
import com.proton.Gopenpgp.crypto.Key
78
import java.io.File
89
import kotlinx.coroutines.CoroutineDispatcher
@@ -16,18 +17,21 @@ public class AndroidKeyManager(
1617

1718
private val keyDir = File(filesDirPath, KeyDir)
1819

19-
override suspend fun addKey(stringKey: String): Result<String, Throwable> = withContext(dispatcher) {
20-
return@withContext addKey(Key(stringKey))
21-
}
20+
override suspend fun addKey(stringKey: String): Result<String, Throwable> =
21+
withContext(dispatcher) {
22+
return@withContext addKey(Crypto.newKeyFromArmored(stringKey))
23+
}
2224

2325
override suspend fun addKey(key: Key): Result<String, Throwable> =
2426
withContext(dispatcher) {
2527
if (!keyDirExists())
2628
return@withContext Err<Throwable>(IllegalStateException("Key directory does not exist"))
2729

28-
return@withContext runCatching {
29-
val keyFile = File(keyDir, "${key.hexKeyID}.key")
30-
keyFile.writeText(key.armor())
30+
return@withContext runCatching {
31+
val keyFile = File(keyDir, "${key.hexKeyID}.key")
32+
if (keyFile.exists()) keyFile.delete()
33+
34+
keyFile.writeText(key.armor())
3135

3236
key.hexKeyID
3337
}

crypto-pgp/src/main/kotlin/dev/msfjarvis/aps/data/crypto/GopenpgpCryptoHandler.kt

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,15 @@
55

66
package dev.msfjarvis.aps.data.crypto
77

8+
import com.github.michaelbull.result.unwrap
89
import com.proton.Gopenpgp.crypto.Crypto
910
import com.proton.Gopenpgp.helper.Helper
1011
import javax.inject.Inject
1112

1213
/** Gopenpgp backed implementation of [CryptoHandler]. */
13-
public class GopenpgpCryptoHandler @Inject constructor() : CryptoHandler {
14+
public class GopenpgpCryptoHandler
15+
@Inject
16+
constructor(private val gpgKeyManager: AndroidKeyManager) : CryptoHandler {
1417

1518
/**
1619
* Decrypt the given [ciphertext] using the given PGP [privateKey] and corresponding [passphrase].
@@ -42,4 +45,24 @@ public class GopenpgpCryptoHandler @Inject constructor() : CryptoHandler {
4245
plaintext,
4346
)
4447
}
48+
49+
/**
50+
* TODO: Find a better place for this method Utility method to decrypt the given [ciphertext]
51+
* using the given PGP key [id] and corresponding [passphrase].
52+
*/
53+
public suspend fun decryptFromKeyId(
54+
id: String,
55+
passphrase: ByteArray,
56+
ciphertext: String,
57+
): ByteArray {
58+
// TODO(aditya): Handle error cases
59+
val key = gpgKeyManager.findKeyById(id).unwrap()
60+
val message = Crypto.newPGPMessageFromArmored(ciphertext)
61+
return Helper.decryptMessageArmored(
62+
key.armor(),
63+
passphrase,
64+
message.armored,
65+
)
66+
.toByteArray()
67+
}
4568
}

0 commit comments

Comments
 (0)