diff options
author | Harsh Shandilya <me@msfjarvis.dev> | 2021-10-23 17:02:50 +0530 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-10-23 17:02:50 +0530 |
commit | aac74ae4515aa1d746f46287029441f5a945c98e (patch) | |
tree | 9d23e06592ecd884d6b58dd089692d9e4224a3f9 /crypto-pgp | |
parent | 21c8653e6815ca34574e783a5ce7ac783b188228 (diff) |
Switch new PGP backend to use PGPainless (#1522)
* crypto-pgpainless: init
* crypto-pgpainless: add an opinionated CryptoHandler impl
* app: migrate to crypto-pgpainless
* crypto-pgp: remove
* github: remove now unused instrumentation tests job
* crypto-common: fixup package names
* wip(crypto-pgpainless): add `PGPKeyPair` and `PGPKeyManager`
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
(cherry picked from commit 02d07e9e797a8600cc8c534a731dfffcc44cfdde)
* crypto-pgpainless: use hex-encoded key IDs
* crypto-pgpainless: replace legacy Gopenpgp-generated key file
* crypto-pgpainless: fix CryptoConstants source set
* crypto-pgpainless: fix tests
* crypto-pgpainless: reinstate PGPKeyManager tests
Co-authored-by: Aditya Wasan <adityawasan55@gmail.com>
Diffstat (limited to 'crypto-pgp')
10 files changed, 0 insertions, 481 deletions
diff --git a/crypto-pgp/api/crypto-pgp.api b/crypto-pgp/api/crypto-pgp.api deleted file mode 100644 index c9b2dde7..00000000 --- a/crypto-pgp/api/crypto-pgp.api +++ /dev/null @@ -1,25 +0,0 @@ -public final class dev/msfjarvis/aps/data/crypto/GPGKeyManager : dev/msfjarvis/aps/data/crypto/KeyManager { - public fun <init> (Ljava/lang/String;Lkotlinx/coroutines/CoroutineDispatcher;)V - public fun addKey (Ldev/msfjarvis/aps/data/crypto/GPGKeyPair;ZLkotlin/coroutines/Continuation;)Ljava/lang/Object; - public synthetic fun addKey (Ldev/msfjarvis/aps/data/crypto/KeyPair;ZLkotlin/coroutines/Continuation;)Ljava/lang/Object; - public fun canHandle (Ljava/lang/String;)Z - public fun getAllKeys (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public fun getKeyById (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public fun removeKey (Ldev/msfjarvis/aps/data/crypto/GPGKeyPair;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public synthetic fun removeKey (Ldev/msfjarvis/aps/data/crypto/KeyPair;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; -} - -public final class dev/msfjarvis/aps/data/crypto/GPGKeyPair : dev/msfjarvis/aps/data/crypto/KeyPair { - public fun <init> (Lcom/proton/Gopenpgp/crypto/Key;)V - public fun getKeyId ()Ljava/lang/String; - public fun getPrivateKey ()[B - public fun getPublicKey ()[B -} - -public final class dev/msfjarvis/aps/data/crypto/GopenpgpCryptoHandler : dev/msfjarvis/aps/data/crypto/CryptoHandler { - public fun <init> ()V - public fun canHandle (Ljava/lang/String;)Z - public fun decrypt (Ljava/lang/String;[B[B)[B - public fun encrypt (Ljava/lang/String;[B)[B -} - diff --git a/crypto-pgp/build.gradle.kts b/crypto-pgp/build.gradle.kts deleted file mode 100644 index 95542b1c..00000000 --- a/crypto-pgp/build.gradle.kts +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved. - * SPDX-License-Identifier: GPL-3.0-only - */ - -plugins { - id("com.android.library") - kotlin("android") - `aps-plugin` -} - -android { - defaultConfig { - testApplicationId = "dev.msfjarvis.aps.cryptopgp.test" - testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - } -} - -dependencies { - api(projects.cryptoCommon) - implementation(libs.androidx.annotation) - implementation(libs.aps.gopenpgp) - implementation(libs.dagger.hilt.core) - implementation(libs.kotlin.coroutines.core) - implementation(libs.thirdparty.kotlinResult) - androidTestImplementation(libs.bundles.testDependencies) - androidTestImplementation(libs.kotlin.coroutines.test) - androidTestImplementation(libs.bundles.androidTestDependencies) -} diff --git a/crypto-pgp/src/androidTest/kotlin/dev/msfjarvis/aps/crypto/GPGKeyManagerTest.kt b/crypto-pgp/src/androidTest/kotlin/dev/msfjarvis/aps/crypto/GPGKeyManagerTest.kt deleted file mode 100644 index 80a13eb5..00000000 --- a/crypto-pgp/src/androidTest/kotlin/dev/msfjarvis/aps/crypto/GPGKeyManagerTest.kt +++ /dev/null @@ -1,165 +0,0 @@ -package dev.msfjarvis.aps.crypto - -import androidx.test.platform.app.InstrumentationRegistry -import com.github.michaelbull.result.unwrap -import com.github.michaelbull.result.unwrapError -import com.proton.Gopenpgp.crypto.Key -import dev.msfjarvis.aps.crypto.utils.CryptoConstants -import dev.msfjarvis.aps.cryptopgp.test.R -import dev.msfjarvis.aps.data.crypto.GPGKeyManager -import dev.msfjarvis.aps.data.crypto.GPGKeyPair -import dev.msfjarvis.aps.data.crypto.KeyManagerException -import java.io.File -import kotlin.test.assertEquals -import kotlin.test.assertIs -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.TestCoroutineDispatcher -import kotlinx.coroutines.test.runBlockingTest -import org.junit.After -import org.junit.Before -import org.junit.Test - -@OptIn(ExperimentalCoroutinesApi::class) -public class GPGKeyManagerTest { - - private val testCoroutineDispatcher = TestCoroutineDispatcher() - private lateinit var gpgKeyManager: GPGKeyManager - private lateinit var key: GPGKeyPair - - @Before - public fun setup() { - gpgKeyManager = GPGKeyManager(getFilesDir().absolutePath, testCoroutineDispatcher) - key = GPGKeyPair(Key(getKey())) - } - - @After - public fun tearDown() { - val filesDir = getFilesDir() - val keysDir = File(filesDir, GPGKeyManager.KEY_DIR_NAME) - - keysDir.deleteRecursively() - } - - @Test - public fun testAddingKey() { - runBlockingTest { - // Check if the key id returned is correct - val keyId = gpgKeyManager.addKey(key).unwrap().getKeyId() - assertEquals(CryptoConstants.KEY_ID, keyId) - - // Check if the keys directory have one file - val keysDir = File(getFilesDir(), GPGKeyManager.KEY_DIR_NAME) - assertEquals(1, keysDir.list()?.size) - - // Check if the file name is correct - val keyFile = keysDir.listFiles()?.first() - assertEquals(keyFile?.name, "$keyId.${GPGKeyManager.KEY_EXTENSION}") - } - } - - @Test - public fun testAddingKeyWithoutReplaceFlag() { - runBlockingTest { - // Check adding the keys twice - gpgKeyManager.addKey(key, false).unwrap() - val error = gpgKeyManager.addKey(key, false).unwrapError() - - assertIs<KeyManagerException.KeyAlreadyExistsException>(error) - } - } - - @Test - public fun testAddingKeyWithReplaceFlag() { - runBlockingTest { - // Check adding the keys twice - gpgKeyManager.addKey(key, true).unwrap() - val keyId = gpgKeyManager.addKey(key, true).unwrap().getKeyId() - - assertEquals(CryptoConstants.KEY_ID, keyId) - } - } - - @Test - public fun testRemovingKey() { - runBlockingTest { - // Add key using KeyManager - gpgKeyManager.addKey(key).unwrap() - - // Check if the key id returned is correct - val keyId = gpgKeyManager.removeKey(key).unwrap().getKeyId() - assertEquals(CryptoConstants.KEY_ID, keyId) - - // Check if the keys directory have 0 files - val keysDir = File(getFilesDir(), GPGKeyManager.KEY_DIR_NAME) - assertEquals(0, keysDir.list()?.size) - } - } - - @Test - public fun testGetExistingKey() { - runBlockingTest { - // Add key using KeyManager - gpgKeyManager.addKey(key).unwrap() - - // Check returned key id matches the expected id and the created key id - val returnedKeyPair = gpgKeyManager.getKeyById(key.getKeyId()).unwrap() - assertEquals(CryptoConstants.KEY_ID, key.getKeyId()) - assertEquals(key.getKeyId(), returnedKeyPair.getKeyId()) - } - } - - @Test - public fun testGetNonExistentKey() { - runBlockingTest { - // Add key using KeyManager - gpgKeyManager.addKey(key).unwrap() - - val randomKeyId = "0x123456789" - - // Check returned key - val error = gpgKeyManager.getKeyById(randomKeyId).unwrapError() - assertIs<KeyManagerException.KeyNotFoundException>(error) - assertEquals("No key found with id: $randomKeyId", error.message) - } - } - - @Test - public fun testFindKeysWithoutAdding() { - runBlockingTest { - // Check returned key - val error = gpgKeyManager.getKeyById("0x123456789").unwrapError() - assertIs<KeyManagerException.NoKeysAvailableException>(error) - assertEquals("No keys were found", error.message) - } - } - - @Test - public fun testGettingAllKeys() { - runBlockingTest { - // TODO: Should we check for more than 1 keys? - // Check if KeyManager returns no key - val noKeyList = gpgKeyManager.getAllKeys().unwrap() - assertEquals(0, noKeyList.size) - - // Add key using KeyManager - gpgKeyManager.addKey(key).unwrap() - - // Check if KeyManager returns one key - val singleKeyList = gpgKeyManager.getAllKeys().unwrap() - assertEquals(1, singleKeyList.size) - } - } - - private companion object { - - fun getFilesDir(): File = InstrumentationRegistry.getInstrumentation().context.filesDir - - fun getKey(): String = - InstrumentationRegistry.getInstrumentation() - .context - .resources - .openRawResource(R.raw.private_key) - .readBytes() - .decodeToString() - } -} diff --git a/crypto-pgp/src/androidTest/kotlin/dev/msfjarvis/aps/crypto/GPGKeyPairTest.kt b/crypto-pgp/src/androidTest/kotlin/dev/msfjarvis/aps/crypto/GPGKeyPairTest.kt deleted file mode 100644 index 2340d9a5..00000000 --- a/crypto-pgp/src/androidTest/kotlin/dev/msfjarvis/aps/crypto/GPGKeyPairTest.kt +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved. - * SPDX-License-Identifier: GPL-3.0-only - */ - -package dev.msfjarvis.aps.crypto - -import androidx.test.platform.app.InstrumentationRegistry -import com.proton.Gopenpgp.crypto.Key -import dev.msfjarvis.aps.crypto.utils.CryptoConstants -import dev.msfjarvis.aps.cryptopgp.test.R -import dev.msfjarvis.aps.data.crypto.GPGKeyPair -import dev.msfjarvis.aps.data.crypto.KeyPairException -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith -import org.junit.Test - -public class GPGKeyPairTest { - - @Test - public fun testIfKeyIdIsCorrect() { - val gpgKey = Key(getKey()) - val keyPair = GPGKeyPair(gpgKey) - - assertEquals(CryptoConstants.KEY_ID, keyPair.getKeyId()) - } - - @Test - public fun testBuildingKeyPairWithoutPrivateKey() { - assertFailsWith<KeyPairException.PrivateKeyUnavailableException>( - "GPGKeyPair does not have a private sub key" - ) { - // Get public key object from private key - val gpgKey = Key(getKey()).toPublic() - // Try creating a KeyPair from public key - val keyPair = GPGKeyPair(gpgKey) - - keyPair.getPrivateKey() - } - } - - private companion object { - - fun getKey(): String = - InstrumentationRegistry.getInstrumentation() - .context - .resources - .openRawResource(R.raw.private_key) - .readBytes() - .decodeToString() - } -} diff --git a/crypto-pgp/src/androidTest/kotlin/dev/msfjarvis/aps/crypto/utils/CryptoConstants.kt b/crypto-pgp/src/androidTest/kotlin/dev/msfjarvis/aps/crypto/utils/CryptoConstants.kt deleted file mode 100644 index 873f7105..00000000 --- a/crypto-pgp/src/androidTest/kotlin/dev/msfjarvis/aps/crypto/utils/CryptoConstants.kt +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved. - * SPDX-License-Identifier: GPL-3.0-only - */ - -package dev.msfjarvis.aps.crypto.utils - -internal object CryptoConstants { - internal const val KEY_PASSPHRASE = "hunter2" - internal const val PLAIN_TEXT = "encryption worthy content" - internal const val KEY_NAME = "John Doe" - internal const val KEY_EMAIL = "john.doe@example.com" - internal const val KEY_ID = "04ace699d5b15b7e" -} diff --git a/crypto-pgp/src/androidTest/res/raw/private_key b/crypto-pgp/src/androidTest/res/raw/private_key deleted file mode 100644 index 5a4f436c..00000000 --- a/crypto-pgp/src/androidTest/res/raw/private_key +++ /dev/null @@ -1,18 +0,0 @@ ------BEGIN PGP PRIVATE KEY BLOCK----- -Version: GopenPGP 2.1.9 -Comment: https://gopenpgp.org - -xYYEYN+EThYJKwYBBAHaRw8BAQdAh0d9GdVyJV6KbFynPz3sHkdi5RDnKYs+l0x0 -rEOEthX+CQMIfg7BTvTTe7pgvNERA1vLXRjSxXyi7tfSV13JRnrapp7YtNUSHLVS -PqbaLBd6+EXx7dJ9mUSUSWVga5mdtLZ/k6e+6dsygeHiJuwxfGbHnc0fSm9obiBE -b2UgPGpvaG4uZG9lQGV4YW1wbGUuY29tPsKIBBMWCAA6BQJg34ROCRAErOaZ1bFb -fhYhBJQ0DPsSHC5XfslyQwSs5pnVsVt+AhsDAh4BAhkBAwsJBwIVCAIiAQAAtgwB -AOa3rnipQPsxgxvOP1V+2kD6ssiwt6BZRWwPcyfeX1h4AP9ozBYr+PSmNbam9bnq -wgXwuQhPJeWTSgILMaiasugGCMeLBGDfhE4SCisGAQQBl1UBBQEBB0ClFQJX/L2G -EX9ucC5mvwj3X/7aDXDFAmIpQeWYSS1negMBCgn+CQMIF1uko+Ym3thgoDWUgM5e -MNmDG3rYkTa7h6mlhhrsYtE/GN78EJHP1ygFzOczU/YdbxSRTZCu697uPCZLWURV -1+b66KLTMNHNaAkoFb2JC8J4BBgWCAAqBQJg34ROCRAErOaZ1bFbfhYhBJQ0DPsS -HC5XfslyQwSs5pnVsVt+AhsMAAB1CgEApNcEivCSp0f8CnV4UCoSRRRekIbP1Ub2 -GJx6iRJR8xwA/jicDxdnl/Umfd3mWjGk04R47whiDOXdwjBmC1KVBaMH -=Sfsa ------END PGP PRIVATE KEY BLOCK-----
\ No newline at end of file diff --git a/crypto-pgp/src/main/AndroidManifest.xml b/crypto-pgp/src/main/AndroidManifest.xml deleted file mode 100644 index f72b702d..00000000 --- a/crypto-pgp/src/main/AndroidManifest.xml +++ /dev/null @@ -1,6 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?><!-- - ~ Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved. - ~ SPDX-License-Identifier: LGPL-3.0-only WITH LGPL-3.0-linking-exception - --> - -<manifest package="dev.msfjarvis.aps.cryptopgp"></manifest> diff --git a/crypto-pgp/src/main/kotlin/dev/msfjarvis/aps/data/crypto/GPGKeyManager.kt b/crypto-pgp/src/main/kotlin/dev/msfjarvis/aps/data/crypto/GPGKeyManager.kt deleted file mode 100644 index 478d2700..00000000 --- a/crypto-pgp/src/main/kotlin/dev/msfjarvis/aps/data/crypto/GPGKeyManager.kt +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved. - * SPDX-License-Identifier: GPL-3.0-only - */ - -package dev.msfjarvis.aps.data.crypto - -import androidx.annotation.VisibleForTesting -import com.github.michaelbull.result.Result -import com.github.michaelbull.result.runCatching -import com.proton.Gopenpgp.crypto.Crypto -import java.io.File -import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.withContext - -public class GPGKeyManager(filesDir: String, private val dispatcher: CoroutineDispatcher) : - KeyManager<GPGKeyPair> { - - private val keyDir = File(filesDir, KEY_DIR_NAME) - - override suspend fun addKey(key: GPGKeyPair, replace: Boolean): Result<GPGKeyPair, Throwable> = - withContext(dispatcher) { - runCatching { - if (!keyDirExists()) throw KeyManagerException.KeyDirectoryUnavailableException - val keyFile = File(keyDir, "${key.getKeyId()}.$KEY_EXTENSION") - if (keyFile.exists()) { - // Check for replace flag first and if it is false, throw an error - if (!replace) throw KeyManagerException.KeyAlreadyExistsException(key.getKeyId()) - if (!keyFile.delete()) throw KeyManagerException.KeyDeletionFailedException - } - - keyFile.writeBytes(key.getPrivateKey()) - - key - } - } - - override suspend fun removeKey(key: GPGKeyPair): Result<GPGKeyPair, Throwable> = - withContext(dispatcher) { - runCatching { - if (!keyDirExists()) throw KeyManagerException.KeyDirectoryUnavailableException - val keyFile = File(keyDir, "${key.getKeyId()}.$KEY_EXTENSION") - if (keyFile.exists()) { - if (!keyFile.delete()) throw KeyManagerException.KeyDeletionFailedException - } - - key - } - } - - override suspend fun getKeyById(id: String): Result<GPGKeyPair, Throwable> = - withContext(dispatcher) { - runCatching { - if (!keyDirExists()) throw KeyManagerException.KeyDirectoryUnavailableException - val keys = keyDir.listFiles() - if (keys.isNullOrEmpty()) throw KeyManagerException.NoKeysAvailableException - - for (keyFile in keys) { - val keyPair = GPGKeyPair(Crypto.newKeyFromArmored(keyFile.readText())) - if (keyPair.getKeyId() == id) return@runCatching keyPair - } - - throw KeyManagerException.KeyNotFoundException(id) - } - } - - override suspend fun getAllKeys(): Result<List<GPGKeyPair>, Throwable> = - withContext(dispatcher) { - runCatching { - if (!keyDirExists()) throw KeyManagerException.KeyDirectoryUnavailableException - val keys = keyDir.listFiles() - if (keys.isNullOrEmpty()) return@runCatching listOf() - - keys.map { GPGKeyPair(Crypto.newKeyFromArmored(it.readText())) }.toList() - } - } - - override fun canHandle(fileName: String): Boolean { - // TODO: This is a temp hack for now and in future it should check that the GPGKeyManager can - // decrypt the file - return fileName.endsWith(KEY_EXTENSION) - } - - private fun keyDirExists(): Boolean { - return keyDir.exists() || keyDir.mkdirs() - } - - internal companion object { - - @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) - internal const val KEY_DIR_NAME: String = "keys" - @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) - internal const val KEY_EXTENSION: String = "key" - } -} diff --git a/crypto-pgp/src/main/kotlin/dev/msfjarvis/aps/data/crypto/GPGKeyPair.kt b/crypto-pgp/src/main/kotlin/dev/msfjarvis/aps/data/crypto/GPGKeyPair.kt deleted file mode 100644 index 2dbe8689..00000000 --- a/crypto-pgp/src/main/kotlin/dev/msfjarvis/aps/data/crypto/GPGKeyPair.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved. - * SPDX-License-Identifier: GPL-3.0-only - */ - -package dev.msfjarvis.aps.data.crypto - -import com.proton.Gopenpgp.crypto.Key - -/** Wraps a Gopenpgp [Key] to implement [KeyPair]. */ -public class GPGKeyPair(private val key: Key) : KeyPair { - - init { - if (!key.isPrivate) throw KeyPairException.PrivateKeyUnavailableException - } - - override fun getPrivateKey(): ByteArray { - return key.armor().encodeToByteArray() - } - - override fun getPublicKey(): ByteArray { - return key.armoredPublicKey.encodeToByteArray() - } - - override fun getKeyId(): String { - return key.hexKeyID - } -} diff --git a/crypto-pgp/src/main/kotlin/dev/msfjarvis/aps/data/crypto/GopenpgpCryptoHandler.kt b/crypto-pgp/src/main/kotlin/dev/msfjarvis/aps/data/crypto/GopenpgpCryptoHandler.kt deleted file mode 100644 index 5d14b160..00000000 --- a/crypto-pgp/src/main/kotlin/dev/msfjarvis/aps/data/crypto/GopenpgpCryptoHandler.kt +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved. - * SPDX-License-Identifier: GPL-3.0-only - */ - -package dev.msfjarvis.aps.data.crypto - -import com.proton.Gopenpgp.crypto.Crypto -import com.proton.Gopenpgp.helper.Helper -import javax.inject.Inject - -/** Gopenpgp backed implementation of [CryptoHandler]. */ -public class GopenpgpCryptoHandler @Inject constructor() : CryptoHandler { - - /** - * Decrypt the given [ciphertext] using the given PGP [privateKey] and corresponding [passphrase]. - */ - override fun decrypt( - privateKey: String, - passphrase: ByteArray, - ciphertext: ByteArray, - ): ByteArray { - // Decode the incoming cipher into a string and try to guess if it's armored. - val cipherString = ciphertext.decodeToString() - val isArmor = cipherString.startsWith("-----BEGIN PGP MESSAGE-----") - val message = - if (isArmor) { - Crypto.newPGPMessageFromArmored(cipherString) - } else { - Crypto.newPGPMessage(ciphertext) - } - return Helper.decryptBinaryMessageArmored( - privateKey, - passphrase, - message.armored, - ) - } - - override fun encrypt(publicKey: String, plaintext: ByteArray): ByteArray { - return Helper.encryptBinaryMessage( - publicKey, - plaintext, - ) - } - - override fun canHandle(fileName: String): Boolean { - return fileName.split('.').last() == "gpg" - } -} |