mirror of
https://github.com/android-password-store/Android-Password-Store
synced 2025-08-30 05:48:09 +00:00
Fix PGPainless backend key handling (#2000)
This commit is contained in:
parent
b7e291450b
commit
d23b0c5d6f
@ -18,4 +18,5 @@ dependencies {
|
|||||||
implementation(libs.thirdparty.pgpainless)
|
implementation(libs.thirdparty.pgpainless)
|
||||||
testImplementation(libs.bundles.testDependencies)
|
testImplementation(libs.bundles.testDependencies)
|
||||||
testImplementation(libs.kotlin.coroutines.test)
|
testImplementation(libs.kotlin.coroutines.test)
|
||||||
|
testImplementation(libs.testing.testparameterinjector)
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,8 @@ import java.io.ByteArrayInputStream
|
|||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
import org.bouncycastle.openpgp.PGPPublicKeyRing
|
||||||
|
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection
|
||||||
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection
|
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection
|
||||||
import org.pgpainless.PGPainless
|
import org.pgpainless.PGPainless
|
||||||
import org.pgpainless.decryption_verification.ConsumerOptions
|
import org.pgpainless.decryption_verification.ConsumerOptions
|
||||||
@ -65,14 +67,25 @@ public class PGPainlessCryptoHandler @Inject constructor() : CryptoHandler<PGPKe
|
|||||||
): Result<Unit, CryptoHandlerException> =
|
): Result<Unit, CryptoHandlerException> =
|
||||||
runCatching {
|
runCatching {
|
||||||
if (keys.isEmpty()) throw NoKeysProvided("No keys provided for encryption")
|
if (keys.isEmpty()) throw NoKeysProvided("No keys provided for encryption")
|
||||||
val armoredKeys = keys.map { key -> key.contents.decodeToString() }
|
val publicKeyRings = arrayListOf<PGPPublicKeyRing>()
|
||||||
val pubKeysStream = ByteArrayInputStream(armoredKeys.joinToString("\n").toByteArray())
|
val armoredKeys =
|
||||||
|
keys.joinToString("\n") { key -> key.contents.decodeToString() }.toByteArray()
|
||||||
|
val secKeysStream = ByteArrayInputStream(armoredKeys)
|
||||||
|
val secretKeyRingCollection =
|
||||||
|
PGPainless.readKeyRing().secretKeyRingCollection(secKeysStream)
|
||||||
|
secretKeyRingCollection.forEach { secretKeyRing ->
|
||||||
|
publicKeyRings.add(PGPainless.extractCertificate(secretKeyRing))
|
||||||
|
}
|
||||||
|
if (publicKeyRings.isEmpty()) {
|
||||||
|
val pubKeysStream = ByteArrayInputStream(armoredKeys)
|
||||||
val publicKeyRingCollection =
|
val publicKeyRingCollection =
|
||||||
pubKeysStream.use { PGPainless.readKeyRing().publicKeyRingCollection(pubKeysStream) }
|
PGPainless.readKeyRing().publicKeyRingCollection(pubKeysStream)
|
||||||
val encryptionOptions =
|
publicKeyRings.addAll(publicKeyRingCollection)
|
||||||
EncryptionOptions.encryptCommunications()
|
}
|
||||||
.addRecipients(publicKeyRingCollection.asIterable())
|
require(publicKeyRings.isNotEmpty()) { "No public keys to encrypt message to" }
|
||||||
val producerOptions = ProducerOptions.encrypt(encryptionOptions).setAsciiArmor(true)
|
val publicKeyRingCollection = PGPPublicKeyRingCollection(publicKeyRings)
|
||||||
|
val encryptionOptions = EncryptionOptions().addRecipients(publicKeyRingCollection)
|
||||||
|
val producerOptions = ProducerOptions.encrypt(encryptionOptions).setAsciiArmor(false)
|
||||||
val encryptor =
|
val encryptor =
|
||||||
PGPainless.encryptAndOrSign().onOutputStream(outputStream).withOptions(producerOptions)
|
PGPainless.encryptAndOrSign().onOutputStream(outputStream).withOptions(producerOptions)
|
||||||
plaintextStream.copyTo(encryptor)
|
plaintextStream.copyTo(encryptor)
|
||||||
@ -83,7 +96,6 @@ public class PGPainlessCryptoHandler @Inject constructor() : CryptoHandler<PGPKe
|
|||||||
"Stream should be encrypted for ${keyRing.publicKey.keyID} but wasn't"
|
"Stream should be encrypted for ${keyRing.publicKey.keyID} but wasn't"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return@runCatching
|
|
||||||
}
|
}
|
||||||
.mapError { error ->
|
.mapError { error ->
|
||||||
when (error) {
|
when (error) {
|
||||||
|
@ -7,6 +7,8 @@ package dev.msfjarvis.aps.crypto
|
|||||||
|
|
||||||
import com.github.michaelbull.result.Err
|
import com.github.michaelbull.result.Err
|
||||||
import com.github.michaelbull.result.getError
|
import com.github.michaelbull.result.getError
|
||||||
|
import com.google.testing.junit.testparameterinjector.TestParameter
|
||||||
|
import com.google.testing.junit.testparameterinjector.TestParameterInjector
|
||||||
import dev.msfjarvis.aps.crypto.errors.IncorrectPassphraseException
|
import dev.msfjarvis.aps.crypto.errors.IncorrectPassphraseException
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
@ -14,18 +16,26 @@ import kotlin.test.assertEquals
|
|||||||
import kotlin.test.assertFalse
|
import kotlin.test.assertFalse
|
||||||
import kotlin.test.assertIs
|
import kotlin.test.assertIs
|
||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
|
||||||
|
@Suppress("Unused") // Test runner handles it internally
|
||||||
|
enum class EncryptionKey(val key: PGPKey) {
|
||||||
|
PUBLIC(PGPKey(TestUtils.getArmoredPublicKey())),
|
||||||
|
SECRET(PGPKey(TestUtils.getArmoredPrivateKey())),
|
||||||
|
}
|
||||||
|
|
||||||
|
@RunWith(TestParameterInjector::class)
|
||||||
class PGPainlessCryptoHandlerTest {
|
class PGPainlessCryptoHandlerTest {
|
||||||
|
|
||||||
|
@TestParameter private lateinit var encryptionKey: EncryptionKey
|
||||||
private val cryptoHandler = PGPainlessCryptoHandler()
|
private val cryptoHandler = PGPainlessCryptoHandler()
|
||||||
private val privateKey = PGPKey(TestUtils.getArmoredPrivateKey())
|
private val privateKey = PGPKey(TestUtils.getArmoredPrivateKey())
|
||||||
private val publicKey = PGPKey(TestUtils.getArmoredPublicKey())
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun encryptAndDecrypt() {
|
fun encryptAndDecrypt() {
|
||||||
val ciphertextStream = ByteArrayOutputStream()
|
val ciphertextStream = ByteArrayOutputStream()
|
||||||
cryptoHandler.encrypt(
|
cryptoHandler.encrypt(
|
||||||
listOf(publicKey),
|
listOf(encryptionKey.key),
|
||||||
CryptoConstants.PLAIN_TEXT.byteInputStream(Charsets.UTF_8),
|
CryptoConstants.PLAIN_TEXT.byteInputStream(Charsets.UTF_8),
|
||||||
ciphertextStream,
|
ciphertextStream,
|
||||||
)
|
)
|
||||||
@ -43,7 +53,7 @@ class PGPainlessCryptoHandlerTest {
|
|||||||
fun decryptWithWrongPassphrase() {
|
fun decryptWithWrongPassphrase() {
|
||||||
val ciphertextStream = ByteArrayOutputStream()
|
val ciphertextStream = ByteArrayOutputStream()
|
||||||
cryptoHandler.encrypt(
|
cryptoHandler.encrypt(
|
||||||
listOf(publicKey),
|
listOf(encryptionKey.key),
|
||||||
CryptoConstants.PLAIN_TEXT.byteInputStream(Charsets.UTF_8),
|
CryptoConstants.PLAIN_TEXT.byteInputStream(Charsets.UTF_8),
|
||||||
ciphertextStream,
|
ciphertextStream,
|
||||||
)
|
)
|
||||||
|
@ -70,6 +70,7 @@ testing-junit = "junit:junit:4.13.2"
|
|||||||
testing-kotlintest-junit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin" }
|
testing-kotlintest-junit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin" }
|
||||||
testing-robolectric = "org.robolectric:robolectric:4.8.1"
|
testing-robolectric = "org.robolectric:robolectric:4.8.1"
|
||||||
testing-sharedPrefsMock = "com.github.android-password-store:shared-preferences-fake:2.0.0"
|
testing-sharedPrefsMock = "com.github.android-password-store:shared-preferences-fake:2.0.0"
|
||||||
|
testing-testparameterinjector = "com.google.testparameterinjector:test-parameter-injector:1.8"
|
||||||
testing-turbine = "app.cash.turbine:turbine:0.8.0"
|
testing-turbine = "app.cash.turbine:turbine:0.8.0"
|
||||||
thirdparty-bouncycastle-bcpkix = { module = "org.bouncycastle:bcpkix-jdk15to18", version.ref = "bouncycastle" }
|
thirdparty-bouncycastle-bcpkix = { module = "org.bouncycastle:bcpkix-jdk15to18", version.ref = "bouncycastle" }
|
||||||
thirdparty-bouncycastle-bcprov = { module = "org.bouncycastle:bcprov-jdk15to18", version.ref = "bouncycastle" }
|
thirdparty-bouncycastle-bcprov = { module = "org.bouncycastle:bcprov-jdk15to18", version.ref = "bouncycastle" }
|
||||||
|
Loading…
x
Reference in New Issue
Block a user