aboutsummaryrefslogtreecommitdiff
path: root/crypto-pgpainless/src/test/kotlin/app
diff options
context:
space:
mode:
Diffstat (limited to 'crypto-pgpainless/src/test/kotlin/app')
-rw-r--r--crypto-pgpainless/src/test/kotlin/app/passwordstore/crypto/CryptoConstants.kt14
-rw-r--r--crypto-pgpainless/src/test/kotlin/app/passwordstore/crypto/GpgIdentifierTest.kt41
-rw-r--r--crypto-pgpainless/src/test/kotlin/app/passwordstore/crypto/KeyUtilsTest.kt24
-rw-r--r--crypto-pgpainless/src/test/kotlin/app/passwordstore/crypto/PGPKeyManagerTest.kt189
-rw-r--r--crypto-pgpainless/src/test/kotlin/app/passwordstore/crypto/PGPainlessCryptoHandlerTest.kt78
-rw-r--r--crypto-pgpainless/src/test/kotlin/app/passwordstore/crypto/TestUtils.kt16
6 files changed, 362 insertions, 0 deletions
diff --git a/crypto-pgpainless/src/test/kotlin/app/passwordstore/crypto/CryptoConstants.kt b/crypto-pgpainless/src/test/kotlin/app/passwordstore/crypto/CryptoConstants.kt
new file mode 100644
index 00000000..d827e169
--- /dev/null
+++ b/crypto-pgpainless/src/test/kotlin/app/passwordstore/crypto/CryptoConstants.kt
@@ -0,0 +1,14 @@
+/*
+ * Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved.
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+package app.passwordstore.crypto
+
+object CryptoConstants {
+ const val KEY_PASSPHRASE = "hunter2"
+ const val PLAIN_TEXT = "encryption worthy content"
+ const val KEY_NAME = "John Doe"
+ const val KEY_EMAIL = "john.doe@example.com"
+ const val KEY_ID = 0x08edf7567183ce27
+}
diff --git a/crypto-pgpainless/src/test/kotlin/app/passwordstore/crypto/GpgIdentifierTest.kt b/crypto-pgpainless/src/test/kotlin/app/passwordstore/crypto/GpgIdentifierTest.kt
new file mode 100644
index 00000000..009976a2
--- /dev/null
+++ b/crypto-pgpainless/src/test/kotlin/app/passwordstore/crypto/GpgIdentifierTest.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright © 2014-2022 The Android Password Store Authors. All Rights Reserved.
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+package app.passwordstore.crypto
+
+import kotlin.test.Test
+import kotlin.test.assertNotNull
+import kotlin.test.assertTrue
+
+class GpgIdentifierTest {
+
+ @Test
+ fun parseHexKeyIdWithout0xPrefix() {
+ val identifier = GpgIdentifier.fromString("79E8208280490C77")
+ assertNotNull(identifier)
+ assertTrue { identifier is GpgIdentifier.KeyId }
+ }
+
+ @Test
+ fun parseHexKeyId() {
+ val identifier = GpgIdentifier.fromString("0x79E8208280490C77")
+ assertNotNull(identifier)
+ assertTrue { identifier is GpgIdentifier.KeyId }
+ }
+
+ @Test
+ fun parseValidEmail() {
+ val identifier = GpgIdentifier.fromString("john.doe@example.org")
+ assertNotNull(identifier)
+ assertTrue { identifier is GpgIdentifier.UserId }
+ }
+
+ @Test
+ fun parseEmailWithoutTLD() {
+ val identifier = GpgIdentifier.fromString("john.doe@example")
+ assertNotNull(identifier)
+ assertTrue { identifier is GpgIdentifier.UserId }
+ }
+}
diff --git a/crypto-pgpainless/src/test/kotlin/app/passwordstore/crypto/KeyUtilsTest.kt b/crypto-pgpainless/src/test/kotlin/app/passwordstore/crypto/KeyUtilsTest.kt
new file mode 100644
index 00000000..209865ea
--- /dev/null
+++ b/crypto-pgpainless/src/test/kotlin/app/passwordstore/crypto/KeyUtilsTest.kt
@@ -0,0 +1,24 @@
+package app.passwordstore.crypto
+
+import app.passwordstore.crypto.KeyUtils.tryGetId
+import app.passwordstore.crypto.KeyUtils.tryParseKeyring
+import app.passwordstore.crypto.TestUtils.getArmoredPrivateKeyWithMultipleIdentities
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertIs
+import kotlin.test.assertNotNull
+import org.bouncycastle.openpgp.PGPSecretKeyRing
+
+class KeyUtilsTest {
+ @Test
+ fun parseKeyWithMultipleIdentities() {
+ val key = PGPKey(getArmoredPrivateKeyWithMultipleIdentities())
+ val keyring = tryParseKeyring(key)
+ assertNotNull(keyring)
+ assertIs<PGPSecretKeyRing>(keyring)
+ val keyId = tryGetId(key)
+ assertNotNull(keyId)
+ assertIs<GpgIdentifier.KeyId>(keyId)
+ assertEquals("b950ae2813841585", keyId.toString())
+ }
+}
diff --git a/crypto-pgpainless/src/test/kotlin/app/passwordstore/crypto/PGPKeyManagerTest.kt b/crypto-pgpainless/src/test/kotlin/app/passwordstore/crypto/PGPKeyManagerTest.kt
new file mode 100644
index 00000000..c446cee4
--- /dev/null
+++ b/crypto-pgpainless/src/test/kotlin/app/passwordstore/crypto/PGPKeyManagerTest.kt
@@ -0,0 +1,189 @@
+package app.passwordstore.crypto
+
+import app.passwordstore.crypto.GpgIdentifier.KeyId
+import app.passwordstore.crypto.GpgIdentifier.UserId
+import app.passwordstore.crypto.TestUtils.getArmoredPrivateKeyWithMultipleIdentities
+import app.passwordstore.crypto.errors.KeyAlreadyExistsException
+import app.passwordstore.crypto.errors.KeyNotFoundException
+import app.passwordstore.crypto.errors.NoKeysAvailableException
+import com.github.michaelbull.result.unwrap
+import com.github.michaelbull.result.unwrapError
+import java.io.File
+import kotlin.test.AfterTest
+import kotlin.test.BeforeTest
+import kotlin.test.Test
+import kotlin.test.assertContentEquals
+import kotlin.test.assertEquals
+import kotlin.test.assertIs
+import kotlin.test.assertNotNull
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.resetMain
+import kotlinx.coroutines.test.runTest
+import kotlinx.coroutines.test.setMain
+import org.junit.Rule
+import org.junit.rules.TemporaryFolder
+
+@OptIn(ExperimentalCoroutinesApi::class)
+class PGPKeyManagerTest {
+
+ @get:Rule val temporaryFolder: TemporaryFolder = TemporaryFolder()
+ private val filesDir by unsafeLazy { temporaryFolder.root }
+ private val keysDir by unsafeLazy { File(filesDir, PGPKeyManager.KEY_DIR_NAME) }
+ private val dispatcher = StandardTestDispatcher()
+ private val scope = TestScope(dispatcher)
+ private val keyManager by unsafeLazy { PGPKeyManager(filesDir.absolutePath, dispatcher) }
+ private val key = PGPKey(TestUtils.getArmoredPrivateKey())
+
+ private fun <T> unsafeLazy(initializer: () -> T) =
+ lazy(LazyThreadSafetyMode.NONE) { initializer.invoke() }
+
+ @BeforeTest
+ fun setUp() {
+ Dispatchers.setMain(dispatcher)
+ }
+
+ @AfterTest
+ fun tearDown() {
+ Dispatchers.resetMain()
+ }
+
+ @Test
+ fun addKey() =
+ scope.runTest {
+ // Check if the key id returned is correct
+ val keyId = keyManager.getKeyId(keyManager.addKey(key).unwrap())
+ assertEquals(KeyId(CryptoConstants.KEY_ID), keyId)
+
+ // Check if the keys directory have one file
+ assertEquals(1, filesDir.list()?.size)
+
+ // Check if the file name is correct
+ val keyFile = keysDir.listFiles()?.first()
+ assertEquals(keyFile?.name, "$keyId.${PGPKeyManager.KEY_EXTENSION}")
+ }
+
+ @Test
+ fun addKeyWithoutReplaceFlag() =
+ scope.runTest {
+ // Check adding the keys twice
+ keyManager.addKey(key, false).unwrap()
+ val error = keyManager.addKey(key, false).unwrapError()
+
+ assertIs<KeyAlreadyExistsException>(error)
+ }
+
+ @Test
+ fun addKeyWithReplaceFlag() =
+ scope.runTest {
+ // Check adding the keys twice
+ keyManager.addKey(key, true).unwrap()
+ val keyId = keyManager.getKeyId(keyManager.addKey(key, true).unwrap())
+
+ assertEquals(KeyId(CryptoConstants.KEY_ID), keyId)
+ }
+
+ @Test
+ fun removeKey() =
+ scope.runTest {
+ // Add key using KeyManager
+ keyManager.addKey(key).unwrap()
+
+ // Check if the key id returned is correct
+ val keyId = keyManager.getKeyId(keyManager.removeKey(key).unwrap())
+ assertEquals(KeyId(CryptoConstants.KEY_ID), keyId)
+
+ // Check if the keys directory have 0 files
+ val keysDir = File(filesDir, PGPKeyManager.KEY_DIR_NAME)
+ assertEquals(0, keysDir.list()?.size)
+ }
+
+ @Test
+ fun getKeyById() =
+ scope.runTest {
+ // Add key using KeyManager
+ keyManager.addKey(key).unwrap()
+
+ val keyId = keyManager.getKeyId(key)
+ assertNotNull(keyId)
+ assertEquals(KeyId(CryptoConstants.KEY_ID), keyManager.getKeyId(key))
+
+ // Check returned key id matches the expected id and the created key id
+ val returnedKey = keyManager.getKeyById(keyId).unwrap()
+ assertEquals(keyManager.getKeyId(key), keyManager.getKeyId(returnedKey))
+ }
+
+ @Test
+ fun getKeyByFullUserId() =
+ scope.runTest {
+ keyManager.addKey(key).unwrap()
+
+ val keyId = "${CryptoConstants.KEY_NAME} <${CryptoConstants.KEY_EMAIL}>"
+ val returnedKey = keyManager.getKeyById(UserId(keyId)).unwrap()
+ assertEquals(keyManager.getKeyId(key), keyManager.getKeyId(returnedKey))
+ }
+
+ @Test
+ fun getKeyByEmailUserId() =
+ scope.runTest {
+ keyManager.addKey(key).unwrap()
+
+ val keyId = CryptoConstants.KEY_EMAIL
+ val returnedKey = keyManager.getKeyById(UserId(keyId)).unwrap()
+ assertEquals(keyManager.getKeyId(key), keyManager.getKeyId(returnedKey))
+ }
+
+ @Test
+ fun getNonExistentKey() =
+ scope.runTest {
+ // Add key using KeyManager
+ keyManager.addKey(key).unwrap()
+
+ val keyId = KeyId(0x08edf7567183ce44)
+
+ // Check returned key
+ val error = keyManager.getKeyById(keyId).unwrapError()
+ assertIs<KeyNotFoundException>(error)
+ assertEquals("No key found with id: $keyId", error.message)
+ }
+
+ @Test
+ fun findNonExistentKey() =
+ scope.runTest {
+ // Check returned key
+ val error = keyManager.getKeyById(KeyId(0x08edf7567183ce44)).unwrapError()
+ assertIs<NoKeysAvailableException>(error)
+ assertEquals("No keys were found", error.message)
+ }
+
+ @Test
+ fun getAllKeys() =
+ scope.runTest {
+ // Check if KeyManager returns no key
+ val noKeyList = keyManager.getAllKeys().unwrap()
+ assertEquals(0, noKeyList.size)
+
+ // Add key using KeyManager
+ keyManager.addKey(key).unwrap()
+ keyManager.addKey(PGPKey(getArmoredPrivateKeyWithMultipleIdentities())).unwrap()
+
+ // Check if KeyManager returns one key
+ val singleKeyList = keyManager.getAllKeys().unwrap()
+ assertEquals(2, singleKeyList.size)
+ }
+
+ @Test
+ fun getMultipleIdentityKeyWithAllIdentities() {
+ scope.runTest {
+ val key = PGPKey(getArmoredPrivateKeyWithMultipleIdentities())
+ keyManager.addKey(key).unwrap()
+
+ val johnKey = keyManager.getKeyById(UserId("john@doe.org")).unwrap()
+ val janeKey = keyManager.getKeyById(UserId("jane@doe.org")).unwrap()
+
+ assertContentEquals(johnKey.contents, janeKey.contents)
+ }
+ }
+}
diff --git a/crypto-pgpainless/src/test/kotlin/app/passwordstore/crypto/PGPainlessCryptoHandlerTest.kt b/crypto-pgpainless/src/test/kotlin/app/passwordstore/crypto/PGPainlessCryptoHandlerTest.kt
new file mode 100644
index 00000000..60e8fb6e
--- /dev/null
+++ b/crypto-pgpainless/src/test/kotlin/app/passwordstore/crypto/PGPainlessCryptoHandlerTest.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved.
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+package app.passwordstore.crypto
+
+import app.passwordstore.crypto.errors.IncorrectPassphraseException
+import com.github.michaelbull.result.Err
+import com.github.michaelbull.result.getError
+import com.google.testing.junit.testparameterinjector.TestParameter
+import com.google.testing.junit.testparameterinjector.TestParameterInjector
+import java.io.ByteArrayOutputStream
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertFalse
+import kotlin.test.assertIs
+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 {
+
+ @TestParameter private lateinit var encryptionKey: EncryptionKey
+ private val cryptoHandler = PGPainlessCryptoHandler()
+ private val privateKey = PGPKey(TestUtils.getArmoredPrivateKey())
+
+ @Test
+ fun encryptAndDecrypt() {
+ val ciphertextStream = ByteArrayOutputStream()
+ cryptoHandler.encrypt(
+ listOf(encryptionKey.key),
+ CryptoConstants.PLAIN_TEXT.byteInputStream(Charsets.UTF_8),
+ ciphertextStream,
+ )
+ val plaintextStream = ByteArrayOutputStream()
+ cryptoHandler.decrypt(
+ privateKey,
+ CryptoConstants.KEY_PASSPHRASE,
+ ciphertextStream.toByteArray().inputStream(),
+ plaintextStream,
+ )
+ assertEquals(CryptoConstants.PLAIN_TEXT, plaintextStream.toString(Charsets.UTF_8))
+ }
+
+ @Test
+ fun decryptWithWrongPassphrase() {
+ val ciphertextStream = ByteArrayOutputStream()
+ cryptoHandler.encrypt(
+ listOf(encryptionKey.key),
+ CryptoConstants.PLAIN_TEXT.byteInputStream(Charsets.UTF_8),
+ ciphertextStream,
+ )
+ val plaintextStream = ByteArrayOutputStream()
+ val result =
+ cryptoHandler.decrypt(
+ privateKey,
+ "very incorrect passphrase",
+ ciphertextStream.toByteArray().inputStream(),
+ plaintextStream,
+ )
+ assertIs<Err<Throwable>>(result)
+ assertIs<IncorrectPassphraseException>(result.getError())
+ }
+
+ @Test
+ 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/app/passwordstore/crypto/TestUtils.kt b/crypto-pgpainless/src/test/kotlin/app/passwordstore/crypto/TestUtils.kt
new file mode 100644
index 00000000..1e5aea8c
--- /dev/null
+++ b/crypto-pgpainless/src/test/kotlin/app/passwordstore/crypto/TestUtils.kt
@@ -0,0 +1,16 @@
+/*
+ * Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved.
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+@file:Suppress("RECEIVER_NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS")
+
+package app.passwordstore.crypto
+
+object TestUtils {
+ fun getArmoredPrivateKey() = this::class.java.classLoader.getResource("private_key").readBytes()
+ fun getArmoredPublicKey() = this::class.java.classLoader.getResource("public_key").readBytes()
+ fun getArmoredPrivateKeyWithMultipleIdentities() =
+ this::class.java.classLoader.getResource("private_key_multiple_identities").readBytes()
+ fun getArmoredPublicKeyWithMultipleIdentities() =
+ this::class.java.classLoader.getResource("public_key_multiple_identities").readBytes()
+}