aboutsummaryrefslogtreecommitdiff
path: root/crypto/common/src
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/common/src')
-rw-r--r--crypto/common/src/main/kotlin/app/passwordstore/crypto/CryptoHandler.kt44
-rw-r--r--crypto/common/src/main/kotlin/app/passwordstore/crypto/CryptoOptions.kt8
-rw-r--r--crypto/common/src/main/kotlin/app/passwordstore/crypto/KeyManager.kt41
-rw-r--r--crypto/common/src/main/kotlin/app/passwordstore/crypto/errors/CryptoException.kt48
4 files changed, 141 insertions, 0 deletions
diff --git a/crypto/common/src/main/kotlin/app/passwordstore/crypto/CryptoHandler.kt b/crypto/common/src/main/kotlin/app/passwordstore/crypto/CryptoHandler.kt
new file mode 100644
index 00000000..898cf058
--- /dev/null
+++ b/crypto/common/src/main/kotlin/app/passwordstore/crypto/CryptoHandler.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.CryptoHandlerException
+import com.github.michaelbull.result.Result
+import java.io.InputStream
+import java.io.OutputStream
+
+/** Generic interface to implement cryptographic operations on top of. */
+public interface CryptoHandler<Key, EncOpts : CryptoOptions, DecryptOpts : CryptoOptions> {
+
+ /**
+ * Decrypt the given [ciphertextStream] using a set of potential [keys] and [passphrase], and
+ * writes the resultant plaintext to [outputStream]. The returned [Result] should be checked to
+ * ensure it is **not** an instance of [com.github.michaelbull.result.Err] before the contents of
+ * [outputStream] are used.
+ */
+ public fun decrypt(
+ keys: List<Key>,
+ passphrase: String,
+ ciphertextStream: InputStream,
+ outputStream: OutputStream,
+ options: DecryptOpts,
+ ): Result<Unit, CryptoHandlerException>
+
+ /**
+ * Encrypt the given [plaintextStream] to the provided [keys], and writes the encrypted ciphertext
+ * to [outputStream]. The returned [Result] should be checked to ensure it is **not** an instance
+ * of [com.github.michaelbull.result.Err] before the contents of [outputStream] are used.
+ */
+ public fun encrypt(
+ keys: List<Key>,
+ plaintextStream: InputStream,
+ outputStream: OutputStream,
+ options: EncOpts,
+ ): Result<Unit, CryptoHandlerException>
+
+ /** Given a [fileName], return whether this instance can handle it. */
+ public fun canHandle(fileName: String): Boolean
+}
diff --git a/crypto/common/src/main/kotlin/app/passwordstore/crypto/CryptoOptions.kt b/crypto/common/src/main/kotlin/app/passwordstore/crypto/CryptoOptions.kt
new file mode 100644
index 00000000..1e60cdba
--- /dev/null
+++ b/crypto/common/src/main/kotlin/app/passwordstore/crypto/CryptoOptions.kt
@@ -0,0 +1,8 @@
+package app.passwordstore.crypto
+
+/** Defines the contract for a grab-bag of options for individual cryptographic operations. */
+public interface CryptoOptions {
+
+ /** Returns a [Boolean] indicating if the [option] is enabled for this operation. */
+ public fun isOptionEnabled(option: String): Boolean
+}
diff --git a/crypto/common/src/main/kotlin/app/passwordstore/crypto/KeyManager.kt b/crypto/common/src/main/kotlin/app/passwordstore/crypto/KeyManager.kt
new file mode 100644
index 00000000..c9db3734
--- /dev/null
+++ b/crypto/common/src/main/kotlin/app/passwordstore/crypto/KeyManager.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved.
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+package app.passwordstore.crypto
+
+import com.github.michaelbull.result.Result
+
+/**
+ * [KeyManager] defines a contract for implementing a management system for [Key]s as they would be
+ * used by an implementation of [CryptoHandler] to obtain eligible public or private keys as
+ * required.
+ */
+public interface KeyManager<Key, KeyIdentifier> {
+
+ /**
+ * Inserts a [key] into the store. If the key already exists, this method will return
+ * [app.passwordstore.crypto.errors.KeyAlreadyExistsException] unless [replace] is `true`.
+ */
+ public suspend fun addKey(key: Key, replace: Boolean = false): Result<Key, Throwable>
+
+ /** Finds a key for [identifier] in the store and deletes it. */
+ public suspend fun removeKey(identifier: KeyIdentifier): Result<Unit, Throwable>
+
+ /**
+ * Get a [Key] for the given [id]. The actual semantics of what [id] is are left to individual
+ * implementations to figure out for themselves. For example, in GPG this can be a full
+ * hexadecimal key ID, an email, a short hex key ID, and probably a few more things.
+ */
+ public suspend fun getKeyById(id: KeyIdentifier): Result<Key, Throwable>
+
+ /** Returns all keys currently in the store as a [List]. */
+ public suspend fun getAllKeys(): Result<List<Key>, Throwable>
+
+ /**
+ * Get a stable identifier for the given [key]. The returned key ID should be suitable to be used
+ * as an identifier for the cryptographic identity tied to this key.
+ */
+ public suspend fun getKeyId(key: Key): KeyIdentifier?
+}
diff --git a/crypto/common/src/main/kotlin/app/passwordstore/crypto/errors/CryptoException.kt b/crypto/common/src/main/kotlin/app/passwordstore/crypto/errors/CryptoException.kt
new file mode 100644
index 00000000..eb64541e
--- /dev/null
+++ b/crypto/common/src/main/kotlin/app/passwordstore/crypto/errors/CryptoException.kt
@@ -0,0 +1,48 @@
+package app.passwordstore.crypto.errors
+
+import app.passwordstore.crypto.KeyManager
+
+public sealed class CryptoException(message: String? = null, cause: Throwable? = null) :
+ Exception(message, cause)
+
+/** Sealed exception types for [KeyManager]. */
+public sealed class KeyManagerException(message: String? = null) : CryptoException(message)
+
+/** Store contains no keys. */
+public data object NoKeysAvailableException : KeyManagerException("No keys were found")
+
+/** Key directory does not exist or cannot be accessed. */
+public data object KeyDirectoryUnavailableException :
+ KeyManagerException("Key directory does not exist")
+
+/** Failed to delete given key. */
+public data object KeyDeletionFailedException : KeyManagerException("Couldn't delete the key file")
+
+/** Failed to parse the key as a known type. */
+public data object InvalidKeyException :
+ KeyManagerException("Given key cannot be parsed as a known key type")
+
+/** Key failed the [app.passwordstore.crypto.KeyUtils.isKeyUsable] test. */
+public data object UnusableKeyException :
+ KeyManagerException("Given key is not usable for encryption - is it using AEAD?")
+
+/** No key matching `keyId` could be found. */
+public class KeyNotFoundException(keyId: String) :
+ KeyManagerException("No key found with id: $keyId")
+
+/** Attempting to add another key for `keyId` without requesting a replace. */
+public class KeyAlreadyExistsException(keyId: String) :
+ KeyManagerException("Pre-existing key was found for $keyId")
+
+/** Sealed exception types for [app.passwordstore.crypto.CryptoHandler]. */
+public sealed class CryptoHandlerException(message: String? = null, cause: Throwable? = null) :
+ CryptoException(message, cause)
+
+/** The passphrase provided for decryption was incorrect. */
+public class IncorrectPassphraseException(cause: Throwable) : CryptoHandlerException(null, cause)
+
+/** No keys were passed to the encrypt/decrypt operation. */
+public data object NoKeysProvidedException : CryptoHandlerException(null, null)
+
+/** An unexpected error that cannot be mapped to a known type. */
+public class UnknownError(cause: Throwable) : CryptoHandlerException(null, cause)