diff options
Diffstat (limited to 'crypto/common/src')
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) |