Reimplement PGPainless encryption logic (#1955)
* crypto-pgpainless: reimplement encryption logic * crypto-pgpainless: add an explicit error type for empty keyset
This commit is contained in:
parent
2ba3a08622
commit
d65fc88a14
2 changed files with 24 additions and 12 deletions
|
@ -37,5 +37,8 @@ public sealed class CryptoHandlerException(message: String? = null, cause: Throw
|
|||
/** The passphrase provided for decryption was incorrect. */
|
||||
public class IncorrectPassphraseException(cause: Throwable) : CryptoHandlerException(null, cause)
|
||||
|
||||
/** No keys were provided for encryption. */
|
||||
public class NoKeysProvided(message: String?) : CryptoHandlerException(message, null)
|
||||
|
||||
/** An unexpected error that cannot be mapped to a known type. */
|
||||
public class UnknownError(cause: Throwable) : CryptoHandlerException(null, cause)
|
||||
|
|
|
@ -10,12 +10,12 @@ import com.github.michaelbull.result.mapError
|
|||
import com.github.michaelbull.result.runCatching
|
||||
import dev.msfjarvis.aps.crypto.errors.CryptoHandlerException
|
||||
import dev.msfjarvis.aps.crypto.errors.IncorrectPassphraseException
|
||||
import dev.msfjarvis.aps.crypto.errors.NoKeysProvided
|
||||
import dev.msfjarvis.aps.crypto.errors.UnknownError
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
import javax.inject.Inject
|
||||
import org.bouncycastle.bcpg.ArmoredInputStream
|
||||
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection
|
||||
import org.pgpainless.PGPainless
|
||||
import org.pgpainless.decryption_verification.ConsumerOptions
|
||||
|
@ -64,24 +64,33 @@ public class PGPainlessCryptoHandler @Inject constructor() : CryptoHandler<PGPKe
|
|||
outputStream: OutputStream,
|
||||
): Result<Unit, CryptoHandlerException> =
|
||||
runCatching {
|
||||
if (keys.isEmpty()) throw NoKeysProvided("No keys provided for encryption")
|
||||
val armoredKeys = keys.map { key -> key.contents.decodeToString() }
|
||||
val pubKeysStream = ByteArrayInputStream(armoredKeys.joinToString("\n").toByteArray())
|
||||
val publicKeyRingCollection =
|
||||
pubKeysStream.use {
|
||||
ArmoredInputStream(it).use { armoredInputStream ->
|
||||
PGPainless.readKeyRing().publicKeyRingCollection(armoredInputStream)
|
||||
}
|
||||
pubKeysStream.use { PGPainless.readKeyRing().publicKeyRingCollection(pubKeysStream) }
|
||||
val encryptionOptions =
|
||||
EncryptionOptions.encryptCommunications()
|
||||
.addRecipients(publicKeyRingCollection.asIterable())
|
||||
val producerOptions = ProducerOptions.encrypt(encryptionOptions).setAsciiArmor(true)
|
||||
val encryptor =
|
||||
PGPainless.encryptAndOrSign().onOutputStream(outputStream).withOptions(producerOptions)
|
||||
plaintextStream.copyTo(encryptor)
|
||||
encryptor.close()
|
||||
val result = encryptor.result
|
||||
publicKeyRingCollection.keyRings.forEach { keyRing ->
|
||||
require(result.isEncryptedFor(keyRing)) {
|
||||
"Stream should be encrypted for ${keyRing.publicKey.keyID} but wasn't"
|
||||
}
|
||||
val encOpt =
|
||||
EncryptionOptions().apply { publicKeyRingCollection.forEach { addRecipient(it) } }
|
||||
val prodOpt = ProducerOptions.encrypt(encOpt).setAsciiArmor(true)
|
||||
PGPainless.encryptAndOrSign().onOutputStream(outputStream).withOptions(prodOpt).use {
|
||||
encryptionStream ->
|
||||
plaintextStream.copyTo(encryptionStream)
|
||||
}
|
||||
return@runCatching
|
||||
}
|
||||
.mapError { error -> UnknownError(error) }
|
||||
.mapError { error ->
|
||||
when (error) {
|
||||
is CryptoHandlerException -> error
|
||||
else -> UnknownError(error)
|
||||
}
|
||||
}
|
||||
|
||||
public override fun canHandle(fileName: String): Boolean {
|
||||
return fileName.split('.').lastOrNull() == "gpg"
|
||||
|
|
Loading…
Reference in a new issue