diff options
7 files changed, 81 insertions, 3 deletions
diff --git a/app/src/main/java/app/passwordstore/data/crypto/CryptoRepository.kt b/app/src/main/java/app/passwordstore/data/crypto/CryptoRepository.kt index b673e94c..233dedef 100644 --- a/app/src/main/java/app/passwordstore/data/crypto/CryptoRepository.kt +++ b/app/src/main/java/app/passwordstore/data/crypto/CryptoRepository.kt @@ -6,6 +6,8 @@ package app.passwordstore.data.crypto import app.passwordstore.crypto.GpgIdentifier +import app.passwordstore.crypto.PGPDecryptOptions +import app.passwordstore.crypto.PGPEncryptOptions import app.passwordstore.crypto.PGPKeyManager import app.passwordstore.crypto.PGPainlessCryptoHandler import app.passwordstore.crypto.errors.CryptoHandlerException @@ -42,8 +44,9 @@ constructor( message: ByteArrayInputStream, out: ByteArrayOutputStream, ): Result<Unit, CryptoHandlerException> { + val decryptionOptions = PGPDecryptOptions.Builder().build() val keys = pgpKeyManager.getAllKeys().unwrap() - return pgpCryptoHandler.decrypt(keys, password, message, out) + return pgpCryptoHandler.decrypt(keys, password, message, out, decryptionOptions) } private suspend fun encryptPgp( @@ -51,11 +54,13 @@ constructor( content: ByteArrayInputStream, out: ByteArrayOutputStream, ): Result<Unit, CryptoHandlerException> { + val encryptionOptions = PGPEncryptOptions.Builder().build() val keys = identities.map { id -> pgpKeyManager.getKeyById(id) }.getAll() return pgpCryptoHandler.encrypt( keys, content, out, + encryptionOptions, ) } } diff --git a/crypto-common/src/main/kotlin/app/passwordstore/crypto/CryptoHandler.kt b/crypto-common/src/main/kotlin/app/passwordstore/crypto/CryptoHandler.kt index ea42af6d..898cf058 100644 --- a/crypto-common/src/main/kotlin/app/passwordstore/crypto/CryptoHandler.kt +++ b/crypto-common/src/main/kotlin/app/passwordstore/crypto/CryptoHandler.kt @@ -11,7 +11,7 @@ import java.io.InputStream import java.io.OutputStream /** Generic interface to implement cryptographic operations on top of. */ -public interface CryptoHandler<Key> { +public interface CryptoHandler<Key, EncOpts : CryptoOptions, DecryptOpts : CryptoOptions> { /** * Decrypt the given [ciphertextStream] using a set of potential [keys] and [passphrase], and @@ -24,6 +24,7 @@ public interface CryptoHandler<Key> { passphrase: String, ciphertextStream: InputStream, outputStream: OutputStream, + options: DecryptOpts, ): Result<Unit, CryptoHandlerException> /** @@ -35,6 +36,7 @@ public interface CryptoHandler<Key> { keys: List<Key>, plaintextStream: InputStream, outputStream: OutputStream, + options: EncOpts, ): Result<Unit, CryptoHandlerException> /** Given a [fileName], return whether this instance can handle it. */ 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-pgpainless/src/main/kotlin/app/passwordstore/crypto/PGPDecryptOptions.kt b/crypto-pgpainless/src/main/kotlin/app/passwordstore/crypto/PGPDecryptOptions.kt new file mode 100644 index 00000000..15ce92f0 --- /dev/null +++ b/crypto-pgpainless/src/main/kotlin/app/passwordstore/crypto/PGPDecryptOptions.kt @@ -0,0 +1,21 @@ +package app.passwordstore.crypto + +/** [CryptoOptions] implementation for PGPainless decrypt operations. */ +public class PGPDecryptOptions +private constructor( + private val values: Map<String, Boolean>, +) : CryptoOptions { + + override fun isOptionEnabled(option: String): Boolean { + return values.getOrDefault(option, false) + } + + /** Implementation of a builder pattern for [PGPDecryptOptions]. */ + public class Builder { + + /** Build the final [PGPDecryptOptions] object. */ + public fun build(): PGPDecryptOptions { + return PGPDecryptOptions(emptyMap()) + } + } +} diff --git a/crypto-pgpainless/src/main/kotlin/app/passwordstore/crypto/PGPEncryptOptions.kt b/crypto-pgpainless/src/main/kotlin/app/passwordstore/crypto/PGPEncryptOptions.kt new file mode 100644 index 00000000..90de6b51 --- /dev/null +++ b/crypto-pgpainless/src/main/kotlin/app/passwordstore/crypto/PGPEncryptOptions.kt @@ -0,0 +1,35 @@ +package app.passwordstore.crypto + +/** [CryptoOptions] implementation for PGPainless encrypt operations. */ +public class PGPEncryptOptions +private constructor( + private val values: Map<String, Boolean>, +) : CryptoOptions { + + internal companion object { + const val ASCII_ARMOR = "ASCII_ARMOR" + } + + override fun isOptionEnabled(option: String): Boolean { + return values.getOrDefault(option, false) + } + + /** Implementation of a builder pattern for [PGPEncryptOptions]. */ + public class Builder { + private val optionsMap = mutableMapOf<String, Boolean>() + + /** + * Toggle whether the encryption operation output will be ASCII armored or in OpenPGP's binary + * format. + */ + public fun withAsciiArmor(enabled: Boolean): Builder { + optionsMap[ASCII_ARMOR] = enabled + return this + } + + /** Build the final [PGPEncryptOptions] object. */ + public fun build(): PGPEncryptOptions { + return PGPEncryptOptions(optionsMap) + } + } +} diff --git a/crypto-pgpainless/src/main/kotlin/app/passwordstore/crypto/PGPainlessCryptoHandler.kt b/crypto-pgpainless/src/main/kotlin/app/passwordstore/crypto/PGPainlessCryptoHandler.kt index faa94dff..91c17004 100644 --- a/crypto-pgpainless/src/main/kotlin/app/passwordstore/crypto/PGPainlessCryptoHandler.kt +++ b/crypto-pgpainless/src/main/kotlin/app/passwordstore/crypto/PGPainlessCryptoHandler.kt @@ -27,13 +27,15 @@ import org.pgpainless.exception.WrongPassphraseException import org.pgpainless.key.protection.SecretKeyRingProtector import org.pgpainless.util.Passphrase -public class PGPainlessCryptoHandler @Inject constructor() : CryptoHandler<PGPKey> { +public class PGPainlessCryptoHandler @Inject constructor() : + CryptoHandler<PGPKey, PGPEncryptOptions, PGPDecryptOptions> { public override fun decrypt( keys: List<PGPKey>, passphrase: String, ciphertextStream: InputStream, outputStream: OutputStream, + options: PGPDecryptOptions, ): Result<Unit, CryptoHandlerException> = runCatching { if (keys.isEmpty()) throw NoKeysProvided("No keys provided for encryption") @@ -63,6 +65,7 @@ public class PGPainlessCryptoHandler @Inject constructor() : CryptoHandler<PGPKe keys: List<PGPKey>, plaintextStream: InputStream, outputStream: OutputStream, + options: PGPEncryptOptions, ): Result<Unit, CryptoHandlerException> = runCatching { if (keys.isEmpty()) throw NoKeysProvided("No keys provided for encryption") diff --git a/crypto-pgpainless/src/test/kotlin/app/passwordstore/crypto/PGPainlessCryptoHandlerTest.kt b/crypto-pgpainless/src/test/kotlin/app/passwordstore/crypto/PGPainlessCryptoHandlerTest.kt index 80c8dc7c..386ca72b 100644 --- a/crypto-pgpainless/src/test/kotlin/app/passwordstore/crypto/PGPainlessCryptoHandlerTest.kt +++ b/crypto-pgpainless/src/test/kotlin/app/passwordstore/crypto/PGPainlessCryptoHandlerTest.kt @@ -41,6 +41,7 @@ class PGPainlessCryptoHandlerTest { encryptionKey.keySet, CryptoConstants.PLAIN_TEXT.byteInputStream(Charsets.UTF_8), ciphertextStream, + PGPEncryptOptions.Builder().build(), ) assertIs<Ok<Unit>>(encryptRes) val plaintextStream = ByteArrayOutputStream() @@ -50,6 +51,7 @@ class PGPainlessCryptoHandlerTest { CryptoConstants.KEY_PASSPHRASE, ciphertextStream.toByteArray().inputStream(), plaintextStream, + PGPDecryptOptions.Builder().build(), ) assertIs<Ok<Unit>>(decryptRes) assertEquals(CryptoConstants.PLAIN_TEXT, plaintextStream.toString(Charsets.UTF_8)) @@ -63,6 +65,7 @@ class PGPainlessCryptoHandlerTest { encryptionKey.keySet, CryptoConstants.PLAIN_TEXT.byteInputStream(Charsets.UTF_8), ciphertextStream, + PGPEncryptOptions.Builder().build(), ) assertIs<Ok<Unit>>(encryptRes) val plaintextStream = ByteArrayOutputStream() @@ -72,6 +75,7 @@ class PGPainlessCryptoHandlerTest { "very incorrect passphrase", ciphertextStream.toByteArray().inputStream(), plaintextStream, + PGPDecryptOptions.Builder().build(), ) assertIs<Err<Throwable>>(result) assertIs<IncorrectPassphraseException>(result.getError()) |