aboutsummaryrefslogtreecommitdiff
path: root/crypto-pgpainless
diff options
context:
space:
mode:
authorHarsh Shandilya <me@msfjarvis.dev>2022-01-09 16:19:52 +0530
committerGitHub <noreply@github.com>2022-01-09 16:19:52 +0530
commit799f1393e49955d05f68b81af26d6cfaf9beadfd (patch)
treee1da29f5c1b3807016bea5f2f1d7046e92529e53 /crypto-pgpainless
parentccb33af854132f1b35b71393ff68d24850de6960 (diff)
Make CryptoHandler use Key as the abstraction layer (#1651)
Diffstat (limited to 'crypto-pgpainless')
-rw-r--r--crypto-pgpainless/src/main/kotlin/dev/msfjarvis/aps/crypto/KeyUtils.kt54
-rw-r--r--crypto-pgpainless/src/main/kotlin/dev/msfjarvis/aps/crypto/PGPKeyManager.kt45
-rw-r--r--crypto-pgpainless/src/main/kotlin/dev/msfjarvis/aps/crypto/PGPainlessCryptoHandler.kt15
-rw-r--r--crypto-pgpainless/src/test/kotlin/dev/msfjarvis/aps/crypto/PGPKeyManagerTest.kt2
-rw-r--r--crypto-pgpainless/src/test/kotlin/dev/msfjarvis/aps/crypto/PGPainlessCryptoHandlerTest.kt11
-rw-r--r--crypto-pgpainless/src/test/kotlin/dev/msfjarvis/aps/crypto/TestUtils.kt3
-rw-r--r--crypto-pgpainless/src/test/resources/public_key21
7 files changed, 94 insertions, 57 deletions
diff --git a/crypto-pgpainless/src/main/kotlin/dev/msfjarvis/aps/crypto/KeyUtils.kt b/crypto-pgpainless/src/main/kotlin/dev/msfjarvis/aps/crypto/KeyUtils.kt
new file mode 100644
index 00000000..e7a3c387
--- /dev/null
+++ b/crypto-pgpainless/src/main/kotlin/dev/msfjarvis/aps/crypto/KeyUtils.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright © 2014-2022 The Android Password Store Authors. All Rights Reserved.
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+package dev.msfjarvis.aps.crypto
+
+import com.github.michaelbull.result.get
+import com.github.michaelbull.result.runCatching
+import java.util.Locale
+import org.bouncycastle.openpgp.PGPKeyRing
+import org.pgpainless.PGPainless
+
+/** Utility methods to deal with PGP [Key]s. */
+public object KeyUtils {
+ /**
+ * Attempts to parse a [PGPKeyRing] from a given [key]. The key is first tried as a secret key and
+ * then as a public one before the method gives up and returns null.
+ */
+ public fun tryParseKeyring(key: Key): PGPKeyRing? {
+ val secKeyRing = runCatching { PGPainless.readKeyRing().secretKeyRing(key.contents) }.get()
+ if (secKeyRing != null) {
+ return secKeyRing
+ }
+ val pubKeyRing = runCatching { PGPainless.readKeyRing().publicKeyRing(key.contents) }.get()
+ if (pubKeyRing != null) {
+ return pubKeyRing
+ }
+ return null
+ }
+
+ /** Parses a [PGPKeyRing] from the given [key] and returns its hex-formatted key ID. */
+ public fun tryGetId(key: Key): String? {
+ val keyRing = tryParseKeyring(key) ?: return null
+ return convertKeyIdToHex(keyRing.publicKey.keyID)
+ }
+
+ /** Convert a [Long] key ID to a formatted string. */
+ private fun convertKeyIdToHex(keyId: Long): String {
+ return "0x" + convertKeyIdToHex32bit(keyId shr 32) + convertKeyIdToHex32bit(keyId)
+ }
+
+ /**
+ * Converts [keyId] to an unsigned [Long] then uses [java.lang.Long.toHexString] to convert it to
+ * a lowercase hex ID.
+ */
+ private fun convertKeyIdToHex32bit(keyId: Long): String {
+ var hexString = java.lang.Long.toHexString(keyId and 0xffffffffL).lowercase(Locale.ENGLISH)
+ while (hexString.length < 8) {
+ hexString = "0$hexString"
+ }
+ return hexString
+ }
+}
diff --git a/crypto-pgpainless/src/main/kotlin/dev/msfjarvis/aps/crypto/PGPKeyManager.kt b/crypto-pgpainless/src/main/kotlin/dev/msfjarvis/aps/crypto/PGPKeyManager.kt
index f1c53721..2053ecd7 100644
--- a/crypto-pgpainless/src/main/kotlin/dev/msfjarvis/aps/crypto/PGPKeyManager.kt
+++ b/crypto-pgpainless/src/main/kotlin/dev/msfjarvis/aps/crypto/PGPKeyManager.kt
@@ -8,15 +8,13 @@ package dev.msfjarvis.aps.crypto
import androidx.annotation.VisibleForTesting
import com.github.michaelbull.result.Result
-import com.github.michaelbull.result.get
import com.github.michaelbull.result.runCatching
+import dev.msfjarvis.aps.crypto.KeyUtils.tryGetId
+import dev.msfjarvis.aps.crypto.KeyUtils.tryParseKeyring
import java.io.File
-import java.util.Locale
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
-import org.bouncycastle.openpgp.PGPKeyRing
-import org.pgpainless.PGPainless
import org.pgpainless.util.selection.userid.SelectUserId
public class PGPKeyManager
@@ -118,45 +116,6 @@ constructor(
return keyDir.exists() || keyDir.mkdirs()
}
- /**
- * Attempts to parse a [PGPKeyRing] from a given [key]. The key is first tried as a secret key and
- * then as a public one before the method gives up and returns null.
- */
- private fun tryParseKeyring(key: Key): PGPKeyRing? {
- val secKeyRing = runCatching { PGPainless.readKeyRing().secretKeyRing(key.contents) }.get()
- if (secKeyRing != null) {
- return secKeyRing
- }
- val pubKeyRing = runCatching { PGPainless.readKeyRing().publicKeyRing(key.contents) }.get()
- if (pubKeyRing != null) {
- return pubKeyRing
- }
- return null
- }
-
- /** Parses a [PGPKeyRing] from the given [key] and returns its hex-formatted key ID. */
- private fun tryGetId(key: Key): String? {
- val keyRing = tryParseKeyring(key) ?: return null
- return convertKeyIdToHex(keyRing.publicKey.keyID)
- }
-
- /** Convert a [Long] key ID to a formatted string. */
- private fun convertKeyIdToHex(keyId: Long): String {
- return "0x" + convertKeyIdToHex32bit(keyId shr 32) + convertKeyIdToHex32bit(keyId)
- }
-
- /**
- * Converts [keyId] to an unsigned [Long] then uses [java.lang.Long.toHexString] to convert it to
- * a lowercase hex ID.
- */
- private fun convertKeyIdToHex32bit(keyId: Long): String {
- var hexString = java.lang.Long.toHexString(keyId and 0xffffffffL).lowercase(Locale.ENGLISH)
- while (hexString.length < 8) {
- hexString = "0$hexString"
- }
- return hexString
- }
-
public companion object {
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
diff --git a/crypto-pgpainless/src/main/kotlin/dev/msfjarvis/aps/crypto/PGPainlessCryptoHandler.kt b/crypto-pgpainless/src/main/kotlin/dev/msfjarvis/aps/crypto/PGPainlessCryptoHandler.kt
index 3276b995..427cd555 100644
--- a/crypto-pgpainless/src/main/kotlin/dev/msfjarvis/aps/crypto/PGPainlessCryptoHandler.kt
+++ b/crypto-pgpainless/src/main/kotlin/dev/msfjarvis/aps/crypto/PGPainlessCryptoHandler.kt
@@ -21,34 +21,35 @@ import org.pgpainless.util.Passphrase
public class PGPainlessCryptoHandler @Inject constructor() : CryptoHandler {
public override fun decrypt(
- privateKey: String,
- password: String,
+ privateKey: Key,
+ passphrase: String,
ciphertextStream: InputStream,
outputStream: OutputStream,
) {
- val pgpSecretKeyRing = PGPainless.readKeyRing().secretKeyRing(privateKey)
+ val pgpSecretKeyRing = PGPainless.readKeyRing().secretKeyRing(privateKey.contents)
val keyringCollection = PGPSecretKeyRingCollection(listOf(pgpSecretKeyRing))
val protector =
PasswordBasedSecretKeyRingProtector.forKey(
pgpSecretKeyRing,
- Passphrase.fromPassword(password)
+ Passphrase.fromPassword(passphrase)
)
PGPainless.decryptAndOrVerify()
.onInputStream(ciphertextStream)
.withOptions(
ConsumerOptions()
.addDecryptionKeys(keyringCollection, protector)
- .addDecryptionPassphrase(Passphrase.fromPassword(password))
+ .addDecryptionPassphrase(Passphrase.fromPassword(passphrase))
)
.use { decryptionStream -> decryptionStream.copyTo(outputStream) }
}
public override fun encrypt(
- pubKeys: List<String>,
+ keys: List<Key>,
plaintextStream: InputStream,
outputStream: OutputStream,
) {
- val pubKeysStream = ByteArrayInputStream(pubKeys.joinToString("\n").toByteArray())
+ val armoredKeys = keys.map { key -> key.contents.decodeToString() }
+ val pubKeysStream = ByteArrayInputStream(armoredKeys.joinToString("\n").toByteArray())
val publicKeyRingCollection =
pubKeysStream.use {
ArmoredInputStream(it).use { armoredInputStream ->
diff --git a/crypto-pgpainless/src/test/kotlin/dev/msfjarvis/aps/crypto/PGPKeyManagerTest.kt b/crypto-pgpainless/src/test/kotlin/dev/msfjarvis/aps/crypto/PGPKeyManagerTest.kt
index c547bdd4..ed2b00c3 100644
--- a/crypto-pgpainless/src/test/kotlin/dev/msfjarvis/aps/crypto/PGPKeyManagerTest.kt
+++ b/crypto-pgpainless/src/test/kotlin/dev/msfjarvis/aps/crypto/PGPKeyManagerTest.kt
@@ -28,7 +28,7 @@ class PGPKeyManagerTest {
private val dispatcher = StandardTestDispatcher()
private val scope = TestScope(dispatcher)
private val keyManager by unsafeLazy { PGPKeyManager(filesDir.absolutePath, dispatcher) }
- private val key = Key(TestUtils.getArmoredPrivateKey().encodeToByteArray())
+ private val key = Key(TestUtils.getArmoredPrivateKey())
private fun <T> unsafeLazy(initializer: () -> T) =
lazy(LazyThreadSafetyMode.NONE) { initializer.invoke() }
diff --git a/crypto-pgpainless/src/test/kotlin/dev/msfjarvis/aps/crypto/PGPainlessCryptoHandlerTest.kt b/crypto-pgpainless/src/test/kotlin/dev/msfjarvis/aps/crypto/PGPainlessCryptoHandlerTest.kt
index c5afeaa2..6ec6ccee 100644
--- a/crypto-pgpainless/src/test/kotlin/dev/msfjarvis/aps/crypto/PGPainlessCryptoHandlerTest.kt
+++ b/crypto-pgpainless/src/test/kotlin/dev/msfjarvis/aps/crypto/PGPainlessCryptoHandlerTest.kt
@@ -14,19 +14,20 @@ import kotlin.test.assertTrue
class PGPainlessCryptoHandlerTest {
private val cryptoHandler = PGPainlessCryptoHandler()
+ private val privateKey = Key(TestUtils.getArmoredPrivateKey())
+ private val publicKey = Key(TestUtils.getArmoredPublicKey())
@Test
- fun encrypt_and_decrypt() {
- val key = TestUtils.getArmoredPrivateKey()
+ fun encryptAndDecrypt() {
val ciphertextStream = ByteArrayOutputStream()
cryptoHandler.encrypt(
- listOf(key),
+ listOf(publicKey),
CryptoConstants.PLAIN_TEXT.byteInputStream(Charsets.UTF_8),
ciphertextStream,
)
val plaintextStream = ByteArrayOutputStream()
cryptoHandler.decrypt(
- key,
+ privateKey,
CryptoConstants.KEY_PASSPHRASE,
ciphertextStream.toByteArray().inputStream(),
plaintextStream,
@@ -35,7 +36,7 @@ class PGPainlessCryptoHandlerTest {
}
@Test
- fun can_handle_filters_formats() {
+ fun canHandleFiltersFormats() {
assertFalse { cryptoHandler.canHandle("example.com") }
assertTrue { cryptoHandler.canHandle("example.com.gpg") }
assertFalse { cryptoHandler.canHandle("example.com.asc") }
diff --git a/crypto-pgpainless/src/test/kotlin/dev/msfjarvis/aps/crypto/TestUtils.kt b/crypto-pgpainless/src/test/kotlin/dev/msfjarvis/aps/crypto/TestUtils.kt
index 1e01da51..ab9b6bf1 100644
--- a/crypto-pgpainless/src/test/kotlin/dev/msfjarvis/aps/crypto/TestUtils.kt
+++ b/crypto-pgpainless/src/test/kotlin/dev/msfjarvis/aps/crypto/TestUtils.kt
@@ -6,5 +6,6 @@
package dev.msfjarvis.aps.crypto
object TestUtils {
- fun getArmoredPrivateKey() = this::class.java.classLoader.getResource("private_key").readText()
+ fun getArmoredPrivateKey() = this::class.java.classLoader.getResource("private_key").readBytes()
+ fun getArmoredPublicKey() = this::class.java.classLoader.getResource("public_key").readBytes()
}
diff --git a/crypto-pgpainless/src/test/resources/public_key b/crypto-pgpainless/src/test/resources/public_key
new file mode 100644
index 00000000..987bac6f
--- /dev/null
+++ b/crypto-pgpainless/src/test/resources/public_key
@@ -0,0 +1,21 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: PGPainless
+Comment: BC98 82EF 93DC 22F8 D7D4 47AD 08ED F756 7183 CE27
+Comment: John Doe <john.doe@example.com>
+
+mDMEYT33+BYJKwYBBAHaRw8BAQdAoofwCvOfKJ4pGxEO4s64wFD+QnePpNY5zXgW
+TTOFb2+0H0pvaG4gRG9lIDxqb2huLmRvZUBleGFtcGxlLmNvbT6IeAQTFgoAIAUC
+YT33+AIbAQUWAgMBAAQLCQgHBRUKCQgLAh4BAhkBAAoJEAjt91Zxg84n5dYA/AiA
+BqBdt2ItWgDPLCNEqt9wIMgRpkDrAMtXXyyLSkWsAQCoowpenGsq5fxhuRcS3w6Q
+s+/Qw1GqnoidxhioR9J+ALg4BGE99/gSCisGAQQBl1UBBQEBB0C7eFVsFUif4q9S
+taBI6JAwsI+hQSAo3I6V4jU3rix8XwMBCAeIdQQYFgoAHQUCYT33+AIbDAUWAgMB
+AAQLCQgHBRUKCQgLAh4BAAoJEAjt91Zxg84nmn4BALmD8WYxTdrJqUZUE1TcFvzG
+5r0//rPM8Vut5X+KwUXjAQDWVP22KaA8VXpevSxkS3n/ti0KjQVKEFzGbmwB2dTT
+CbgzBGE99/gWCSsGAQQB2kcPAQEHQJXfqDjCO9L4qBu62/UPpQ5q0638kG8+AGf/
+hJH2q2BTiNUEGBYKAH0FAmE99/gCGwIFFgIDAQAECwkIBwUVCgkICwIeAV8gBBkW
+CgAGBQJhPff4AAoJEGSLoii3QC8mrhcBALzpJQTHF8cJJRA9+DQ3qZ85Eu217MJi
+x1aYA1i0zyP5AQD/jN/aBsSTqAHF+zU8/ezzHeoilyBYgxLS9Q2qelDeDAAKCRAI
+7fdWcYPOJ7aHAP9EBq0rzV3c6GtVl8bPnk+llpV/1aodxTSnijQtVSMuMAD+JMUD
+Jd2bimlhuVwpu0DFiF7IF64SAxmVifTwsTWYiQs=
+=jGlC
+-----END PGP PUBLIC KEY BLOCK-----