aboutsummaryrefslogtreecommitdiff
path: root/crypto-pgpainless/src/main
diff options
context:
space:
mode:
authorHarsh Shandilya <me@msfjarvis.dev>2023-08-10 03:21:47 +0530
committerHarsh Shandilya <me@msfjarvis.dev>2023-08-10 03:31:08 +0530
commit959a56d7ffbc20db60721d7a09f399c8bdefe07e (patch)
tree0d9b14e0bb770fd6da2e04295f2c22c087142439 /crypto-pgpainless/src/main
parentefef72b6327c8e683c8844146e23d12104f12dd1 (diff)
refactor: un-flatten module structure
Diffstat (limited to 'crypto-pgpainless/src/main')
-rw-r--r--crypto-pgpainless/src/main/kotlin/app/passwordstore/crypto/KeyUtils.kt52
-rw-r--r--crypto-pgpainless/src/main/kotlin/app/passwordstore/crypto/PGPDecryptOptions.kt21
-rw-r--r--crypto-pgpainless/src/main/kotlin/app/passwordstore/crypto/PGPEncryptOptions.kt35
-rw-r--r--crypto-pgpainless/src/main/kotlin/app/passwordstore/crypto/PGPIdentifier.kt128
-rw-r--r--crypto-pgpainless/src/main/kotlin/app/passwordstore/crypto/PGPKey.kt12
-rw-r--r--crypto-pgpainless/src/main/kotlin/app/passwordstore/crypto/PGPKeyManager.kt154
-rw-r--r--crypto-pgpainless/src/main/kotlin/app/passwordstore/crypto/PGPainlessCryptoHandler.kt139
7 files changed, 0 insertions, 541 deletions
diff --git a/crypto-pgpainless/src/main/kotlin/app/passwordstore/crypto/KeyUtils.kt b/crypto-pgpainless/src/main/kotlin/app/passwordstore/crypto/KeyUtils.kt
deleted file mode 100644
index 5b23dc18..00000000
--- a/crypto-pgpainless/src/main/kotlin/app/passwordstore/crypto/KeyUtils.kt
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright © 2014-2022 The Android Password Store Authors. All Rights Reserved.
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-package app.passwordstore.crypto
-
-import app.passwordstore.crypto.PGPIdentifier.KeyId
-import app.passwordstore.crypto.PGPIdentifier.UserId
-import com.github.michaelbull.result.get
-import com.github.michaelbull.result.runCatching
-import org.bouncycastle.openpgp.PGPKeyRing
-import org.pgpainless.PGPainless
-import org.pgpainless.key.parsing.KeyRingReader
-
-/** Utility methods to deal with [PGPKey]s. */
-public object KeyUtils {
- /**
- * Attempts to parse a [PGPKeyRing] from a given [key]. The key is first tried as a secret key and
- * then as a public one before the method gives up and returns null.
- */
- public fun tryParseKeyring(key: PGPKey): PGPKeyRing? {
- return runCatching { KeyRingReader.readKeyRing(key.contents.inputStream()) }.get()
- }
-
- /** Parses a [PGPKeyRing] from the given [key] and calculates its long key ID */
- public fun tryGetId(key: PGPKey): KeyId? {
- val keyRing = tryParseKeyring(key) ?: return null
- return KeyId(keyRing.publicKey.keyID)
- }
-
- /**
- * Attempts to parse the given [PGPKey] into a [PGPKeyRing] and obtains the [UserId] of the
- * corresponding public key.
- */
- public fun tryGetEmail(key: PGPKey): UserId? {
- val keyRing = tryParseKeyring(key) ?: return null
- return UserId(keyRing.publicKey.userIDs.next())
- }
-
- /**
- * Tests if the given [key] can be used for encryption, which is a bare minimum necessity for the
- * app.
- */
- public fun isKeyUsable(key: PGPKey): Boolean {
- return runCatching {
- val keyRing = tryParseKeyring(key) ?: return false
- PGPainless.inspectKeyRing(keyRing).isUsableForEncryption
- }
- .get() != null
- }
-}
diff --git a/crypto-pgpainless/src/main/kotlin/app/passwordstore/crypto/PGPDecryptOptions.kt b/crypto-pgpainless/src/main/kotlin/app/passwordstore/crypto/PGPDecryptOptions.kt
deleted file mode 100644
index 15ce92f0..00000000
--- a/crypto-pgpainless/src/main/kotlin/app/passwordstore/crypto/PGPDecryptOptions.kt
+++ /dev/null
@@ -1,21 +0,0 @@
-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
deleted file mode 100644
index 90de6b51..00000000
--- a/crypto-pgpainless/src/main/kotlin/app/passwordstore/crypto/PGPEncryptOptions.kt
+++ /dev/null
@@ -1,35 +0,0 @@
-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/PGPIdentifier.kt b/crypto-pgpainless/src/main/kotlin/app/passwordstore/crypto/PGPIdentifier.kt
deleted file mode 100644
index 98a1de2e..00000000
--- a/crypto-pgpainless/src/main/kotlin/app/passwordstore/crypto/PGPIdentifier.kt
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright © 2014-2022 The Android Password Store Authors. All Rights Reserved.
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-package app.passwordstore.crypto
-
-import java.util.Locale
-import java.util.regex.Pattern
-
-/** Supertype for valid identifiers of PGP keys. */
-public sealed class PGPIdentifier {
-
- /** A [PGPIdentifier] that represents either a long key ID or a fingerprint. */
- public data class KeyId(val id: Long) : PGPIdentifier() {
- override fun toString(): String {
- return convertKeyIdToHex(id)
- }
-
- /** Convert a [Long] key ID to a formatted string. */
- private fun convertKeyIdToHex(keyId: Long): String {
- return convertKeyIdToHex32bit(keyId shr HEX_32_BIT_COUNT) + convertKeyIdToHex32bit(keyId)
- }
-
- /**
- * Converts [keyId] to an unsigned [Long] then uses [java.lang.Long.toHexString] to convert it
- * to a lowercase hex ID.
- */
- private fun convertKeyIdToHex32bit(keyId: Long): String {
- var hexString = java.lang.Long.toHexString(keyId and HEX_32_BITMASK).lowercase(Locale.ENGLISH)
- while (hexString.length < HEX_32_STRING_LENGTH) {
- hexString = "0$hexString"
- }
- return hexString
- }
- }
-
- /**
- * A [PGPIdentifier] that represents the textual name/email combination corresponding to a key.
- * Despite the [email] property in this class, the value is not guaranteed to be a valid email.
- */
- public data class UserId(val email: String) : PGPIdentifier() {
- override fun toString(): String {
- return email
- }
- }
-
- public companion object {
- private const val HEX_RADIX = 16
- private const val HEX_32_BIT_COUNT = 32
- private const val HEX_32_BITMASK = 0xffffffffL
- private const val HEX_32_STRING_LENGTH = 8
- private const val TRUNCATED_FINGERPRINT_LENGTH = 16
-
- /**
- * Attempts to parse an untyped String identifier into a concrete subtype of [PGPIdentifier].
- */
- @Suppress("ReturnCount")
- public fun fromString(identifier: String): PGPIdentifier? {
- if (identifier.isEmpty()) return null
- // Match long key IDs:
- // FF22334455667788 or 0xFF22334455667788
- val maybeLongKeyId =
- identifier.removePrefix("0x").takeIf { it.matches("[a-fA-F\\d]{16}".toRegex()) }
- if (maybeLongKeyId != null) {
- val keyId = maybeLongKeyId.toULong(HEX_RADIX)
- return KeyId(keyId.toLong())
- }
-
- // Match fingerprints:
- // FF223344556677889900112233445566778899 or 0xFF223344556677889900112233445566778899
- val maybeFingerprint =
- identifier.removePrefix("0x").takeIf { it.matches("[a-fA-F\\d]{40}".toRegex()) }
- if (maybeFingerprint != null) {
- // Truncating to the long key ID is not a security issue since OpenKeychain only
- // accepts
- // non-ambiguous key IDs.
- val keyId = maybeFingerprint.takeLast(TRUNCATED_FINGERPRINT_LENGTH).toULong(HEX_RADIX)
- return KeyId(keyId.toLong())
- }
-
- return splitUserId(identifier)?.let { UserId(it) }
- }
-
- private object UserIdRegex {
- val PATTERN: Pattern = Pattern.compile("^(.*?)(?: \\((.*)\\))?(?: <(.*)>)?$")
- const val NAME = 1
- const val EMAIL = 3
- }
-
- private object EmailRegex {
- val PATTERN: Pattern = Pattern.compile("^<?\"?([^<>\"]*@[^<>\"]*[.]?[^<>\"]*)\"?>?$")
- const val EMAIL = 1
- }
-
- /**
- * Takes a 'Name (Comment) <Email>' user ID in any of its permutations and attempts to extract
- * an email from it.
- */
- @Suppress("NestedBlockDepth")
- private fun splitUserId(userId: String): String? {
- if (userId.isNotEmpty()) {
- val matcher = UserIdRegex.PATTERN.matcher(userId)
- if (matcher.matches()) {
- var name =
- if (matcher.group(UserIdRegex.NAME)?.isEmpty() == true) null
- else matcher.group(UserIdRegex.NAME)
- var email = matcher.group(UserIdRegex.EMAIL)
- if (email != null && name != null) {
- val emailMatcher = EmailRegex.PATTERN.matcher(name)
- if (emailMatcher.matches() && email == emailMatcher.group(EmailRegex.EMAIL)) {
- email = emailMatcher.group(EmailRegex.EMAIL)
- name = null
- }
- }
- if (email == null && name != null) {
- val emailMatcher = EmailRegex.PATTERN.matcher(name)
- if (emailMatcher.matches()) {
- email = emailMatcher.group(EmailRegex.EMAIL)
- }
- }
- return email
- }
- }
- return null
- }
- }
-}
diff --git a/crypto-pgpainless/src/main/kotlin/app/passwordstore/crypto/PGPKey.kt b/crypto-pgpainless/src/main/kotlin/app/passwordstore/crypto/PGPKey.kt
deleted file mode 100644
index a33655d4..00000000
--- a/crypto-pgpainless/src/main/kotlin/app/passwordstore/crypto/PGPKey.kt
+++ /dev/null
@@ -1,12 +0,0 @@
-/*
- * Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved.
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-package app.passwordstore.crypto
-
-/**
- * A simple value class wrapping over a [ByteArray] that can represent a PGP key. The implementation
- * details of public/ private parts as well as identities are deferred to [PGPKeyManager].
- */
-@JvmInline public value class PGPKey(public val contents: ByteArray)
diff --git a/crypto-pgpainless/src/main/kotlin/app/passwordstore/crypto/PGPKeyManager.kt b/crypto-pgpainless/src/main/kotlin/app/passwordstore/crypto/PGPKeyManager.kt
deleted file mode 100644
index aed1acf2..00000000
--- a/crypto-pgpainless/src/main/kotlin/app/passwordstore/crypto/PGPKeyManager.kt
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved.
- * SPDX-License-Identifier: GPL-3.0-only
- */
-@file:Suppress("BlockingMethodInNonBlockingContext")
-
-package app.passwordstore.crypto
-
-import androidx.annotation.VisibleForTesting
-import app.passwordstore.crypto.KeyUtils.isKeyUsable
-import app.passwordstore.crypto.KeyUtils.tryGetId
-import app.passwordstore.crypto.KeyUtils.tryParseKeyring
-import app.passwordstore.crypto.errors.InvalidKeyException
-import app.passwordstore.crypto.errors.KeyAlreadyExistsException
-import app.passwordstore.crypto.errors.KeyDeletionFailedException
-import app.passwordstore.crypto.errors.KeyDirectoryUnavailableException
-import app.passwordstore.crypto.errors.KeyNotFoundException
-import app.passwordstore.crypto.errors.NoKeysAvailableException
-import app.passwordstore.crypto.errors.UnusableKeyException
-import app.passwordstore.util.coroutines.runSuspendCatching
-import com.github.michaelbull.result.Result
-import com.github.michaelbull.result.unwrap
-import java.io.File
-import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.withContext
-import org.bouncycastle.openpgp.PGPPublicKeyRing
-import org.bouncycastle.openpgp.PGPSecretKeyRing
-import org.pgpainless.PGPainless
-import org.pgpainless.util.selection.userid.SelectUserId
-
-public class PGPKeyManager
-@Inject
-constructor(
- filesDir: String,
- private val dispatcher: CoroutineDispatcher,
-) : KeyManager<PGPKey, PGPIdentifier> {
-
- private val keyDir = File(filesDir, KEY_DIR_NAME)
-
- /** @see KeyManager.addKey */
- override suspend fun addKey(key: PGPKey, replace: Boolean): Result<PGPKey, Throwable> =
- withContext(dispatcher) {
- runSuspendCatching {
- if (!keyDirExists()) throw KeyDirectoryUnavailableException
- val incomingKeyRing = tryParseKeyring(key) ?: throw InvalidKeyException
- if (!isKeyUsable(key)) throw UnusableKeyException
- val keyFile = File(keyDir, "${tryGetId(key)}.$KEY_EXTENSION")
- if (keyFile.exists()) {
- val existingKeyBytes = keyFile.readBytes()
- val existingKeyRing =
- tryParseKeyring(PGPKey(existingKeyBytes)) ?: throw InvalidKeyException
- when {
- existingKeyRing is PGPPublicKeyRing && incomingKeyRing is PGPSecretKeyRing -> {
- keyFile.writeBytes(key.contents)
- return@runSuspendCatching key
- }
- existingKeyRing is PGPPublicKeyRing && incomingKeyRing is PGPPublicKeyRing -> {
- val updatedPublicKey = PGPainless.mergeCertificate(existingKeyRing, incomingKeyRing)
- val keyBytes = PGPainless.asciiArmor(updatedPublicKey).encodeToByteArray()
- keyFile.writeBytes(keyBytes)
- return@runSuspendCatching key
- }
- }
- // Check for replace flag first and if it is false, throw an error
- if (!replace)
- throw KeyAlreadyExistsException(
- tryGetId(key)?.toString() ?: "Failed to retrieve key ID"
- )
- if (!keyFile.delete()) throw KeyDeletionFailedException
- }
-
- keyFile.writeBytes(key.contents)
-
- key
- }
- }
-
- /** @see KeyManager.removeKey */
- override suspend fun removeKey(identifier: PGPIdentifier): Result<Unit, Throwable> =
- withContext(dispatcher) {
- runSuspendCatching {
- if (!keyDirExists()) throw KeyDirectoryUnavailableException
- val key = getKeyById(identifier).unwrap()
- val keyFile = File(keyDir, "${tryGetId(key)}.$KEY_EXTENSION")
- if (keyFile.exists()) {
- if (!keyFile.delete()) throw KeyDeletionFailedException
- }
- }
- }
-
- /** @see KeyManager.getKeyById */
- override suspend fun getKeyById(id: PGPIdentifier): Result<PGPKey, Throwable> =
- withContext(dispatcher) {
- runSuspendCatching {
- if (!keyDirExists()) throw KeyDirectoryUnavailableException
- val keyFiles = keyDir.listFiles()
- if (keyFiles.isNullOrEmpty()) throw NoKeysAvailableException
- val keys = keyFiles.map { file -> PGPKey(file.readBytes()) }
-
- val matchResult =
- when (id) {
- is PGPIdentifier.KeyId -> {
- val keyIdMatch =
- keys
- .map { key -> key to tryGetId(key) }
- .firstOrNull { (_, keyId) -> keyId?.id == id.id }
- keyIdMatch?.first
- }
- is PGPIdentifier.UserId -> {
- val selector = SelectUserId.byEmail(id.email)
- val userIdMatch =
- keys
- .map { key -> key to tryParseKeyring(key) }
- .firstOrNull { (_, keyRing) -> selector.firstMatch(keyRing) != null }
- userIdMatch?.first
- }
- }
-
- if (matchResult != null) {
- return@runSuspendCatching matchResult
- }
-
- throw KeyNotFoundException("$id")
- }
- }
-
- /** @see KeyManager.getAllKeys */
- override suspend fun getAllKeys(): Result<List<PGPKey>, Throwable> =
- withContext(dispatcher) {
- runSuspendCatching {
- if (!keyDirExists()) throw KeyDirectoryUnavailableException
- val keyFiles = keyDir.listFiles()
- if (keyFiles.isNullOrEmpty()) return@runSuspendCatching emptyList()
- keyFiles.map { keyFile -> PGPKey(keyFile.readBytes()) }.toList()
- }
- }
-
- /** @see KeyManager.getKeyById */
- override suspend fun getKeyId(key: PGPKey): PGPIdentifier? = tryGetId(key)
-
- /** Checks if [keyDir] exists and attempts to create it if not. */
- private fun keyDirExists(): Boolean {
- return keyDir.exists() || keyDir.mkdirs()
- }
-
- public 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-pgpainless/src/main/kotlin/app/passwordstore/crypto/PGPainlessCryptoHandler.kt b/crypto-pgpainless/src/main/kotlin/app/passwordstore/crypto/PGPainlessCryptoHandler.kt
deleted file mode 100644
index a7087acf..00000000
--- a/crypto-pgpainless/src/main/kotlin/app/passwordstore/crypto/PGPainlessCryptoHandler.kt
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * 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 app.passwordstore.crypto.errors.IncorrectPassphraseException
-import app.passwordstore.crypto.errors.NoKeysProvidedException
-import app.passwordstore.crypto.errors.UnknownError
-import com.github.michaelbull.result.Result
-import com.github.michaelbull.result.mapError
-import com.github.michaelbull.result.runCatching
-import java.io.InputStream
-import java.io.OutputStream
-import javax.inject.Inject
-import org.bouncycastle.openpgp.PGPPublicKeyRing
-import org.bouncycastle.openpgp.PGPPublicKeyRingCollection
-import org.bouncycastle.openpgp.PGPSecretKeyRing
-import org.bouncycastle.openpgp.PGPSecretKeyRingCollection
-import org.bouncycastle.util.io.Streams
-import org.pgpainless.PGPainless
-import org.pgpainless.decryption_verification.ConsumerOptions
-import org.pgpainless.encryption_signing.EncryptionOptions
-import org.pgpainless.encryption_signing.ProducerOptions
-import org.pgpainless.exception.WrongPassphraseException
-import org.pgpainless.key.protection.SecretKeyRingProtector
-import org.pgpainless.util.Passphrase
-
-public class PGPainlessCryptoHandler @Inject constructor() :
- CryptoHandler<PGPKey, PGPEncryptOptions, PGPDecryptOptions> {
-
- /**
- * Decrypts the given [ciphertextStream] using [PGPainless] and writes the decrypted output to
- * [outputStream]. The provided [passphrase] is wrapped in a [SecretKeyRingProtector] and the
- * [keys] argument is defensively checked to ensure it has at least one key present.
- *
- * @see CryptoHandler.decrypt
- */
- public override fun decrypt(
- keys: List<PGPKey>,
- passphrase: String,
- ciphertextStream: InputStream,
- outputStream: OutputStream,
- options: PGPDecryptOptions,
- ): Result<Unit, CryptoHandlerException> =
- runCatching {
- if (keys.isEmpty()) {
- throw NoKeysProvidedException
- }
- val keyringCollection =
- keys
- .map { key -> PGPainless.readKeyRing().secretKeyRing(key.contents) }
- .run(::PGPSecretKeyRingCollection)
- val protector = SecretKeyRingProtector.unlockAnyKeyWith(Passphrase.fromPassword(passphrase))
- val decryptionStream =
- PGPainless.decryptAndOrVerify()
- .onInputStream(ciphertextStream)
- .withOptions(
- ConsumerOptions()
- .addDecryptionKeys(keyringCollection, protector)
- .addDecryptionPassphrase(Passphrase.fromPassword(passphrase))
- )
- Streams.pipeAll(decryptionStream, outputStream)
- decryptionStream.close()
- keyringCollection.forEach { keyRing ->
- check(decryptionStream.metadata.isEncryptedFor(keyRing)) {
- "Stream should be encrypted for ${keyRing.secretKey.keyID} but wasn't"
- }
- }
- return@runCatching
- }
- .mapError { error ->
- when (error) {
- is WrongPassphraseException -> IncorrectPassphraseException(error)
- is CryptoHandlerException -> error
- else -> UnknownError(error)
- }
- }
-
- /**
- * Encrypts the provided [plaintextStream] and writes the encrypted output to [outputStream]. The
- * [keys] argument is defensively checked to contain at least one key.
- *
- * @see CryptoHandler.encrypt
- */
- public override fun encrypt(
- keys: List<PGPKey>,
- plaintextStream: InputStream,
- outputStream: OutputStream,
- options: PGPEncryptOptions,
- ): Result<Unit, CryptoHandlerException> =
- runCatching {
- if (keys.isEmpty()) {
- throw NoKeysProvidedException
- }
- val publicKeyRings =
- keys.mapNotNull(KeyUtils::tryParseKeyring).mapNotNull { keyRing ->
- when (keyRing) {
- is PGPPublicKeyRing -> keyRing
- is PGPSecretKeyRing -> PGPainless.extractCertificate(keyRing)
- else -> null
- }
- }
- require(keys.size == publicKeyRings.size) {
- "Failed to parse all keys: ${keys.size} keys were provided but only ${publicKeyRings.size} were valid"
- }
- if (publicKeyRings.isEmpty()) {
- throw NoKeysProvidedException
- }
- val publicKeyRingCollection = PGPPublicKeyRingCollection(publicKeyRings)
- val encryptionOptions = EncryptionOptions().addRecipients(publicKeyRingCollection)
- val producerOptions =
- ProducerOptions.encrypt(encryptionOptions)
- .setAsciiArmor(options.isOptionEnabled(PGPEncryptOptions.ASCII_ARMOR))
- val encryptionStream =
- PGPainless.encryptAndOrSign().onOutputStream(outputStream).withOptions(producerOptions)
- Streams.pipeAll(plaintextStream, encryptionStream)
- encryptionStream.close()
- val result = encryptionStream.result
- publicKeyRingCollection.forEach { keyRing ->
- require(result.isEncryptedFor(keyRing)) {
- "Stream should be encrypted for ${keyRing.publicKey.keyID} but wasn't"
- }
- }
- }
- .mapError { error ->
- when (error) {
- is CryptoHandlerException -> error
- else -> UnknownError(error)
- }
- }
-
- /** Runs a naive check on the extension for the given [fileName] to check if it is a PGP file. */
- public override fun canHandle(fileName: String): Boolean {
- return fileName.substringAfterLast('.', "") == "gpg"
- }
-}